pi-lens 3.8.73 → 3.8.74
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/CHANGELOG.md +1116 -0
- package/README.md +37 -21
- package/dist/clients/atomic-write.js +3 -3
- package/dist/clients/bus-events-logger.js +5 -1
- package/dist/clients/cache/rule-cache.js +43 -2
- package/dist/clients/cache-observability.js +211 -0
- package/dist/clients/call-graph.js +20 -0
- package/dist/clients/cascade-logger.js +5 -1
- package/dist/clients/complexity-client.js +4 -7
- package/dist/clients/dependency-checker.js +151 -20
- package/dist/clients/diagnostic-dispositions.js +22 -2
- package/dist/clients/dispatch/auxiliary-lsp.js +34 -0
- package/dist/clients/dispatch/dispatcher.js +16 -1
- package/dist/clients/dispatch/fact-store.js +11 -0
- package/dist/clients/dispatch/facts/function-facts.js +14 -0
- package/dist/clients/dispatch/facts/import-facts.js +100 -101
- package/dist/clients/dispatch/facts/tree-sitter-facts.js +5 -8
- package/dist/clients/dispatch/facts/try-catch-facts.js +13 -0
- package/dist/clients/dispatch/integration.js +94 -15
- package/dist/clients/dispatch/rules/async-noise.js +1 -0
- package/dist/clients/dispatch/rules/unsafe-boundary.js +7 -0
- package/dist/clients/dispatch/runners/ast-grep-napi.js +24 -1
- package/dist/clients/dispatch/runners/psscriptanalyzer.js +10 -1
- package/dist/clients/dispatch/runners/tree-sitter.js +153 -166
- package/dist/clients/dispatch/runners/utils/runner-helpers.js +17 -18
- package/dist/clients/feature-hints.js +53 -7
- package/dist/clients/file-kinds.js +86 -31
- package/dist/clients/fix-worklog.js +14 -2
- package/dist/clients/generated-artifacts.js +21 -0
- package/dist/clients/gzip-stage-write.js +84 -0
- package/dist/clients/installer/index.js +358 -129
- package/dist/clients/knip-client.js +64 -1
- package/dist/clients/language-profile.js +61 -72
- package/dist/clients/latency-logger.js +6 -16
- package/dist/clients/lens-config.js +128 -165
- package/dist/clients/lens-engine.js +39 -5
- package/dist/clients/lens-flag-registry.js +285 -0
- package/dist/clients/lens-map.js +2 -7
- package/dist/clients/log-cleanup.js +6 -17
- package/dist/clients/lsp/cascade-tier.js +22 -2
- package/dist/clients/lsp/client.js +74 -10
- package/dist/clients/lsp/config.js +62 -9
- package/dist/clients/lsp/index.js +34 -24
- package/dist/clients/lsp/language.js +4 -1
- package/dist/clients/lsp/launch.js +8 -6
- package/dist/clients/lsp/server.js +84 -29
- package/dist/clients/lsp-document-symbols.js +112 -0
- package/dist/clients/mcp/analyze.js +5 -4
- package/dist/clients/mcp/review.js +36 -3
- package/dist/clients/module-report.js +100 -80
- package/dist/clients/ndjson-logger.js +51 -21
- package/dist/clients/path-keyed-map.js +76 -0
- package/dist/clients/path-utils.js +35 -0
- package/dist/clients/performance-report.js +327 -0
- package/dist/clients/pipeline.js +7 -0
- package/dist/clients/project-changes.js +77 -4
- package/dist/clients/project-diagnostics/extractors.js +26 -115
- package/dist/clients/project-diagnostics/fresh-fetch.js +149 -19
- package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +109 -0
- package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +37 -5
- package/dist/clients/project-diagnostics/scanner.js +247 -127
- package/dist/clients/project-lens-config.js +53 -26
- package/dist/clients/project-report.js +39 -14
- package/dist/clients/project-scale.js +8 -10
- package/dist/clients/project-snapshot-persist-worker.js +6 -0
- package/dist/clients/project-snapshot.js +611 -7
- package/dist/clients/quiet-window.js +9 -1
- package/dist/clients/read-expansion.js +22 -12
- package/dist/clients/read-guard.js +78 -0
- package/dist/clients/redact/secrets.js +449 -0
- package/dist/clients/resource-sampler.js +133 -14
- package/dist/clients/reverse-deps.js +48 -0
- package/dist/clients/review-graph/builder.js +1522 -179
- package/dist/clients/review-graph/persist-worker.js +10 -0
- package/dist/clients/review-graph/query.js +5 -0
- package/dist/clients/review-graph/shared-extraction-ir.js +90 -0
- package/dist/clients/review-graph-logger.js +24 -0
- package/dist/clients/runtime-agent-end.js +4 -2
- package/dist/clients/runtime-coordinator.js +2 -2
- package/dist/clients/runtime-session.js +433 -59
- package/dist/clients/runtime-tool-call.js +46 -14
- package/dist/clients/runtime-turn.js +97 -26
- package/dist/clients/safe-spawn.js +102 -15
- package/dist/clients/session-start-observability.js +15 -0
- package/dist/clients/session-state-store.js +2 -1
- package/dist/clients/sessionstart-logger.js +18 -0
- package/dist/clients/sg-runner.js +48 -33
- package/dist/clients/source-filter.js +49 -25
- package/dist/clients/startup-marker.js +6 -0
- package/dist/clients/startup-timing.js +9 -0
- package/dist/clients/todo-scanner.js +30 -3
- package/dist/clients/tree-sitter-cache.js +147 -139
- package/dist/clients/tree-sitter-client.js +1340 -67
- package/dist/clients/tree-sitter-logger.js +43 -1
- package/dist/clients/tree-sitter-query-loader.js +110 -11
- package/dist/clients/tree-sitter-shared.js +43 -19
- package/dist/clients/tree-sitter-symbol-extractor.js +71 -4
- package/dist/clients/widget-state.js +50 -14
- package/dist/clients/word-index-logger.js +44 -0
- package/dist/clients/word-index.js +252 -35
- package/dist/clients/workspace-topology.js +11 -1
- package/dist/index.js +45544 -39255
- package/dist/mcp/analyze-cli.js +3 -0
- package/dist/mcp/cli.js +110 -0
- package/dist/mcp/server.js +63 -8
- package/dist/tools/lens-diagnostics.js +47 -5
- package/dist/tools/lsp-navigation.js +5 -28
- package/dist/tools/symbol-search.js +21 -2
- package/docs/agent-guide.md +247 -0
- package/docs/astplayground.md +3 -4
- package/docs/audit1.md +5 -5
- package/docs/environment-variables.md +153 -11
- package/docs/features.md +4 -3
- package/docs/globalconfig.md +56 -4
- package/docs/language-coverage.md +2 -2
- package/docs/mcp.md +17 -16
- package/docs/servercapabilities.md +5 -2
- package/docs/settings.md +181 -0
- package/docs/tools.md +14 -0
- package/docs/tree-sitter_rules_catalog.md +11 -31
- package/docs/usage.md +13 -0
- package/package.json +139 -138
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-js-test.yml +1 -0
- package/rules/ast-grep-rules/rule-tests/hardcoded-url-test.yml +2 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-case-declarations-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-js-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/no-javascript-url-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-equality-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-js-test.yml +3 -0
- package/rules/ast-grep-rules/rule-tests/strict-inequality-test.yml +4 -0
- package/rules/ast-grep-rules/rules/hardcoded-url-js.yml +22 -4
- package/rules/ast-grep-rules/rules/hardcoded-url.yml +22 -4
- package/rules/ast-grep-rules/rules/no-case-declarations-js.yml +4 -3
- package/rules/ast-grep-rules/rules/no-case-declarations.yml +4 -3
- package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +10 -0
- package/rules/ast-grep-rules/rules/no-console-except-error.yml +10 -0
- package/rules/ast-grep-rules/rules/no-javascript-url-js.yml +42 -6
- package/rules/ast-grep-rules/rules/no-javascript-url.yml +42 -6
- package/rules/ast-grep-rules/rules/strict-equality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-equality.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality-js.yml +14 -1
- package/rules/ast-grep-rules/rules/strict-inequality.yml +14 -1
- package/rules/tree-sitter-queries/cpp/no-memset-sensitive-data.yml +9 -3
- package/rules/tree-sitter-queries/cpp/no-scoped-lock-without-args.yml +6 -6
- package/rules/tree-sitter-queries/css/calc-spacing.yml +8 -6
- package/rules/tree-sitter-queries/go/go-mutex-copy.yml +21 -18
- package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +3 -3
- package/rules/tree-sitter-queries/java/infinite-loop-java.yml +3 -3
- package/rules/tree-sitter-queries/java/junit-call-super.yml +6 -4
- package/rules/tree-sitter-queries/java/no-double-checked-locking.yml +10 -8
- package/rules/tree-sitter-queries/java/no-field-shadowing.yml +1 -1
- package/rules/tree-sitter-queries/java/prepared-statement-valid-indices.yml +2 -3
- package/rules/tree-sitter-queries/java/resources-closed.yml +2 -2
- package/rules/tree-sitter-queries/java/springboot-default-package.yml +6 -3
- package/rules/tree-sitter-queries/java/switch-fall-through.yml +6 -4
- package/rules/tree-sitter-queries/java/switch-non-case-labels.yml +4 -3
- package/rules/tree-sitter-queries/java/tests-include-assertions.yml +6 -5
- package/rules/tree-sitter-queries/javascript/switch-case-termination-js.yml +5 -8
- package/rules/tree-sitter-queries/javascript/switch-non-case-labels.yml +3 -2
- package/rules/tree-sitter-queries/kotlin/prepared-statement-indices.yml +9 -5
- package/rules/tree-sitter-queries/php/this-in-static-context.yml +5 -5
- package/rules/tree-sitter-queries/python/exit-signature-check.yml +2 -9
- package/rules/tree-sitter-queries/python/in-operator-unsupported.yml +3 -4
- package/rules/tree-sitter-queries/python/no-super-torchscript.yml +4 -8
- package/rules/tree-sitter-queries/python/notimplemented-boolean-context.yml +1 -5
- package/rules/tree-sitter-queries/python/python-empty-except.yml +1 -1
- package/rules/tree-sitter-queries/python/return-in-init.yml +0 -2
- package/rules/tree-sitter-queries/python/yield-return-outside-function.yml +0 -3
- package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +1 -12
- package/rules/tree-sitter-queries/ruby/ruby-eval.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +4 -4
- package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +4 -11
- package/rules/tree-sitter-queries/ruby/ruby-open-struct.yml +1 -1
- package/rules/tree-sitter-queries/ruby/ruby-string-eval.yml +2 -3
- package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +4 -4
- package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +4 -3
- package/rules/tree-sitter-queries/typescript/console-statement.yml +11 -0
- package/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +4 -9
- package/rules/tree-sitter-queries/typescript/empty-switch-case.yml +13 -9
- package/rules/tree-sitter-queries/typescript/infinite-loop.yml +2 -2
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +10 -1
- package/rules/tree-sitter-queries/typescript/switch-case-termination.yml +5 -8
- package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +7 -10
- package/rules/tree-sitter-queries/typescript/unsafe-regex.yml +4 -2
- package/rules/typos/_typos.toml +17 -0
- package/skills/pi-lens-write-tree-sitter-rule/SKILL.md +5 -0
- package/dist/clients/config-enabled-shape.js +0 -39
- package/docs/env_variables.md +0 -10
- package/rules/tree-sitter-queries/abap-disabled/delete-where.yml +0 -46
- package/rules/tree-sitter-queries/cpp/no-confused-move-forward.yml +0 -59
- package/rules/tree-sitter-queries/cpp/noexcept-functions.yml +0 -58
- package/rules/tree-sitter-queries/java/mockito-initialized.yml +0 -66
- package/rules/tree-sitter-queries/java/no-wait-notify-on-thread.yml +0 -58
- package/rules/tree-sitter-queries/java/short-circuit-logic.yml +0 -57
- package/rules/tree-sitter-queries/java/spring-session-attributes-setcomplete.yml +0 -75
- package/rules/tree-sitter-queries/plsql-disabled/delete-update-where.yml +0 -56
- package/rules/tree-sitter-queries/plsql-disabled/end-loop-semicolon.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/fetch-bulk-collect-limit.yml +0 -47
- package/rules/tree-sitter-queries/plsql-disabled/forallsave-exceptions.yml +0 -51
- package/rules/tree-sitter-queries/plsql-disabled/nchar-nvarchar2-bytes.yml +0 -54
- package/rules/tree-sitter-queries/plsql-disabled/not-null-initialization.yml +0 -59
- package/rules/tree-sitter-queries/plsql-disabled/raise-application-error-codes.yml +0 -50
- package/rules/tree-sitter-queries/typescript-disabled/ts-path-traversal.yml +0 -71
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
pi-lens gives AI coding agents fast, language-aware feedback while they write/edit.
|
|
8
8
|
|
|
9
|
+
> **Working in this project as an AI agent?** Read the [agent guide](docs/agent-guide.md)
|
|
10
|
+
> for how pi-lens surfaces diagnostics (honesty labels, blockers, read-before-edit)
|
|
11
|
+
> and how to respond.
|
|
12
|
+
|
|
9
13
|
## What It Does
|
|
10
14
|
|
|
11
15
|
- LSP diagnostics and navigation across supported languages
|
|
@@ -47,6 +51,7 @@ pi install git:github.com/apmantza/pi-lens
|
|
|
47
51
|
|
|
48
52
|
## Documentation
|
|
49
53
|
|
|
54
|
+
- [Agent guide](docs/agent-guide.md) — how an AI agent should consume and respond to pi-lens
|
|
50
55
|
- [Agent tools](docs/agent-tools.md) — pi tool names, scopes, and arguments
|
|
51
56
|
- [Usage guide](docs/usage.md) — lifecycle, tool behavior, MCP notes, and
|
|
52
57
|
troubleshooting
|
|
@@ -56,9 +61,11 @@ pi install git:github.com/apmantza/pi-lens
|
|
|
56
61
|
- [Tools and commands](docs/tools.md) — runtime flags and slash commands
|
|
57
62
|
- [Diagnostic dispositions](docs/dispositions.md) — triage: false-positive,
|
|
58
63
|
suppress, defer, flagged-to-fix
|
|
64
|
+
- [Settings](docs/settings.md) — the configuration hub: defaults, env vars, CLI
|
|
65
|
+
flags, and global vs project config at a glance
|
|
59
66
|
- [Configuration](docs/globalconfig.md) — global and project config files
|
|
60
|
-
- [Environment variables](docs/
|
|
61
|
-
reference
|
|
67
|
+
- [Environment variables](docs/environment-variables.md) — common env vars and
|
|
68
|
+
full reference
|
|
62
69
|
- [Language coverage](docs/language-coverage.md) — supported languages, runners,
|
|
63
70
|
and formatters
|
|
64
71
|
- [Dependencies](docs/dependencies.md) — auto-install policy and external tools
|
|
@@ -82,120 +89,123 @@ Thanks goes to these wonderful people:
|
|
|
82
89
|
<tbody>
|
|
83
90
|
<tr>
|
|
84
91
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/apmantza"><img src="https://avatars.githubusercontent.com/u/247365598?v=4" width="100px;" alt=""/><br /><sub><b>Apostolos Mantzaris</b></sub></a><br /><a href="#code-apmantza" title="Code">💻</a> <a href="#doc-apmantza" title="Documentation">📖</a> <a href="#ideas-apmantza" title="Ideas & Planning">🤔</a> <a href="#maintenance-apmantza" title="Maintenance">🚧</a> <a href="#review-apmantza" title="Reviewed Pull Requests">👀</a></td>
|
|
92
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AngriestBird"><img src="https://avatars.githubusercontent.com/u/12663269?v=4" width="100px;" alt=""/><br /><sub><b>Kenneth McCormick</b></sub></a><br /><a href="#bug-AngriestBird" title="Bug reports">🐛</a> <a href="#code-AngriestBird" title="Code">💻</a></td>
|
|
85
93
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anh-chu"><img src="https://avatars.githubusercontent.com/u/34973633?v=4" width="100px;" alt=""/><br /><sub><b>Anh Chu</b></sub></a><br /><a href="#code-anh-chu" title="Code">💻</a></td>
|
|
86
94
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/loss-and-quick"><img src="https://avatars.githubusercontent.com/u/39405619?v=4" width="100px;" alt=""/><br /><sub><b>minicx</b></sub></a><br /><a href="#code-loss-and-quick" title="Code">💻</a></td>
|
|
87
95
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/silvanshade"><img src="https://avatars.githubusercontent.com/u/11022302?v=4" width="100px;" alt=""/><br /><sub><b>silvanshade</b></sub></a><br /><a href="#code-silvanshade" title="Code">💻</a></td>
|
|
88
96
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tifandotme"><img src="https://avatars.githubusercontent.com/u/33323177?v=4" width="100px;" alt=""/><br /><sub><b>Tifan Dwi Avianto</b></sub></a><br /><a href="#code-tifandotme" title="Code">💻</a></td>
|
|
89
97
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amit-gshe"><img src="https://avatars.githubusercontent.com/u/7383028?v=4" width="100px;" alt=""/><br /><sub><b>Amit</b></sub></a><br /><a href="#code-amit-gshe" title="Code">💻</a> <a href="#bug-amit-gshe" title="Bug reports">🐛</a></td>
|
|
90
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fractiunate"><img src="https://avatars.githubusercontent.com/u/78024279?v=4" width="100px;" alt=""/><br /><sub><b>Fractiunate // David Rahäuser</b></sub></a><br /><a href="#code-fractiunate" title="Code">💻</a></td>
|
|
91
98
|
</tr>
|
|
92
99
|
<tr>
|
|
100
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fractiunate"><img src="https://avatars.githubusercontent.com/u/78024279?v=4" width="100px;" alt=""/><br /><sub><b>Fractiunate // David Rahäuser</b></sub></a><br /><a href="#code-fractiunate" title="Code">💻</a></td>
|
|
93
101
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/vkarasen"><img src="https://avatars.githubusercontent.com/u/7932536?v=4" width="100px;" alt=""/><br /><sub><b>Vitali Karasenko</b></sub></a><br /><a href="#code-vkarasen" title="Code">💻</a></td>
|
|
94
102
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rfxlamia"><img src="https://avatars.githubusercontent.com/u/222023708?v=4" width="100px;" alt=""/><br /><sub><b>V</b></sub></a><br /><a href="#code-rfxlamia" title="Code">💻</a></td>
|
|
95
103
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fabio-dee"><img src="https://avatars.githubusercontent.com/u/10808443?v=4" width="100px;" alt=""/><br /><sub><b>Fabio Dee</b></sub></a><br /><a href="#code-fabio-dee" title="Code">💻</a></td>
|
|
96
104
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/moj02090"><img src="https://avatars.githubusercontent.com/u/57255166?v=4" width="100px;" alt=""/><br /><sub><b>moj02090</b></sub></a><br /><a href="#code-moj02090" title="Code">💻</a></td>
|
|
97
105
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mehalter"><img src="https://avatars.githubusercontent.com/u/1591837?v=4" width="100px;" alt=""/><br /><sub><b>Micah Halter</b></sub></a><br /><a href="#code-mehalter" title="Code">💻</a></td>
|
|
98
106
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JulienEllie"><img src="https://avatars.githubusercontent.com/u/670518?v=4" width="100px;" alt=""/><br /><sub><b>Julien Ellie</b></sub></a><br /><a href="#code-JulienEllie" title="Code">💻</a></td>
|
|
99
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Istar-Eldritch"><img src="https://avatars.githubusercontent.com/u/3746468?v=4" width="100px;" alt=""/><br /><sub><b>Ruben Paz</b></sub></a><br /><a href="#code-Istar-Eldritch" title="Code">💻</a> <a href="#bug-Istar-Eldritch" title="Bug reports">🐛</a></td>
|
|
100
107
|
</tr>
|
|
101
108
|
<tr>
|
|
109
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Istar-Eldritch"><img src="https://avatars.githubusercontent.com/u/3746468?v=4" width="100px;" alt=""/><br /><sub><b>Ruben Paz</b></sub></a><br /><a href="#code-Istar-Eldritch" title="Code">💻</a> <a href="#bug-Istar-Eldritch" title="Bug reports">🐛</a></td>
|
|
102
110
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cjunxiang"><img src="https://avatars.githubusercontent.com/u/26619858?v=4" width="100px;" alt=""/><br /><sub><b>C.Junxiang</b></sub></a><br /><a href="#code-cjunxiang" title="Code">💻</a> <a href="#bug-cjunxiang" title="Bug reports">🐛</a></td>
|
|
103
111
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/trvon"><img src="https://avatars.githubusercontent.com/u/6031322?v=4" width="100px;" alt=""/><br /><sub><b>Trevon</b></sub></a><br /><a href="#code-trvon" title="Code">💻</a></td>
|
|
104
112
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cmptr"><img src="https://avatars.githubusercontent.com/u/32746889?v=4" width="100px;" alt=""/><br /><sub><b>Aaron Bell</b></sub></a><br /><a href="#code-cmptr" title="Code">💻</a> <a href="#bug-cmptr" title="Bug reports">🐛</a></td>
|
|
105
113
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/StartupBros"><img src="https://avatars.githubusercontent.com/u/16693591?v=4" width="100px;" alt=""/><br /><sub><b>Will Mitchell</b></sub></a><br /><a href="#code-StartupBros" title="Code">💻</a></td>
|
|
106
114
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/spyrosbazios"><img src="https://avatars.githubusercontent.com/u/37960233?v=4" width="100px;" alt=""/><br /><sub><b>spyrosbazios</b></sub></a><br /><a href="#code-spyrosbazios" title="Code">💻</a></td>
|
|
107
115
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Roman-Galeev"><img src="https://avatars.githubusercontent.com/u/40388226?v=4" width="100px;" alt=""/><br /><sub><b>Roman Galeev</b></sub></a><br /><a href="#code-Roman-Galeev" title="Code">💻</a></td>
|
|
108
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jerryfan"><img src="https://avatars.githubusercontent.com/u/2540814?v=4" width="100px;" alt=""/><br /><sub><b>jerryfan</b></sub></a><br /><a href="#code-jerryfan" title="Code">💻</a></td>
|
|
109
116
|
</tr>
|
|
110
117
|
<tr>
|
|
118
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jerryfan"><img src="https://avatars.githubusercontent.com/u/2540814?v=4" width="100px;" alt=""/><br /><sub><b>jerryfan</b></sub></a><br /><a href="#code-jerryfan" title="Code">💻</a></td>
|
|
111
119
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shaDmx"><img src="https://avatars.githubusercontent.com/u/91132641?v=4" width="100px;" alt=""/><br /><sub><b>Max L.</b></sub></a><br /><a href="#code-shaDmx" title="Code">💻</a></td>
|
|
112
120
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/radge"><img src="https://avatars.githubusercontent.com/u/129205?v=4" width="100px;" alt=""/><br /><sub><b>David Ryan</b></sub></a><br /><a href="#code-radge" title="Code">💻</a></td>
|
|
113
121
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JayceFreeman"><img src="https://avatars.githubusercontent.com/u/92962110?v=4" width="100px;" alt=""/><br /><sub><b>JayceFreeman</b></sub></a><br /><a href="#code-JayceFreeman" title="Code">💻</a></td>
|
|
114
122
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bloodf"><img src="https://avatars.githubusercontent.com/u/1626923?v=4" width="100px;" alt=""/><br /><sub><b>Heitor Ramon Ribeiro</b></sub></a><br /><a href="#code-bloodf" title="Code">💻</a></td>
|
|
115
123
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/feoh"><img src="https://avatars.githubusercontent.com/u/330070?v=4" width="100px;" alt=""/><br /><sub><b>Chris Patti</b></sub></a><br /><a href="#code-feoh" title="Code">💻</a> <a href="#bug-feoh" title="Bug reports">🐛</a></td>
|
|
116
124
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chenxin-yan"><img src="https://avatars.githubusercontent.com/u/71162231?v=4" width="100px;" alt=""/><br /><sub><b>Chenxin Yan</b></sub></a><br /><a href="#code-chenxin-yan" title="Code">💻</a> <a href="#doc-chenxin-yan" title="Documentation">📖</a> <a href="#bug-chenxin-yan" title="Bug reports">🐛</a></td>
|
|
117
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/flowing-abyss"><img src="https://avatars.githubusercontent.com/u/98622217?v=4" width="100px;" alt=""/><br /><sub><b>flowing-abyss</b></sub></a><br /><a href="#code-flowing-abyss" title="Code">💻</a></td>
|
|
118
125
|
</tr>
|
|
119
126
|
<tr>
|
|
127
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/flowing-abyss"><img src="https://avatars.githubusercontent.com/u/98622217?v=4" width="100px;" alt=""/><br /><sub><b>flowing-abyss</b></sub></a><br /><a href="#code-flowing-abyss" title="Code">💻</a></td>
|
|
120
128
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/shaochong-theoai"><img src="https://avatars.githubusercontent.com/u/305592060?v=4" width="100px;" alt=""/><br /><sub><b>shaochong-theoai</b></sub></a><br /><a href="#code-shaochong-theoai" title="Code">💻</a></td>
|
|
121
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuli500177"><img src="https://avatars.githubusercontent.com/u/62830942?v=4" width="100px;" alt=""/><br /><sub><b>Xu Yili</b></sub></a><br /><a href="#bug-xuli500177" title="Bug reports">🐛</a></td>
|
|
129
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xuli500177"><img src="https://avatars.githubusercontent.com/u/62830942?v=4" width="100px;" alt=""/><br /><sub><b>Xu Yili</b></sub></a><br /><a href="#bug-xuli500177" title="Bug reports">🐛</a> <a href="#code-xuli500177" title="Code">💻</a></td>
|
|
122
130
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ricardoraposo"><img src="https://avatars.githubusercontent.com/u/50217712?v=4" width="100px;" alt=""/><br /><sub><b>Ricardo Raposo</b></sub></a><br /><a href="#code-ricardoraposo" title="Code">💻</a></td>
|
|
123
131
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jsmestad"><img src="https://avatars.githubusercontent.com/u/16150?v=4" width="100px;" alt=""/><br /><sub><b>Justin Smestad</b></sub></a><br /><a href="#code-jsmestad" title="Code">💻</a></td>
|
|
124
132
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/anasalsbey-glitch"><img src="https://avatars.githubusercontent.com/u/283609702?v=4" width="100px;" alt=""/><br /><sub><b>anasalsbey-glitch</b></sub></a><br /><a href="#code-anasalsbey-glitch" title="Code">💻</a> <a href="#bug-anasalsbey-glitch" title="Bug reports">🐛</a></td>
|
|
125
133
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PeraSite"><img src="https://avatars.githubusercontent.com/u/19837403?v=4" width="100px;" alt=""/><br /><sub><b>정제훈</b></sub></a><br /><a href="#code-PeraSite" title="Code">💻</a></td>
|
|
126
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carolitascl"><img src="https://avatars.githubusercontent.com/u/26188349?v=4" width="100px;" alt=""/><br /><sub><b>Carolina</b></sub></a><br /><a href="#bug-carolitascl" title="Bug reports">🐛</a></td>
|
|
127
134
|
</tr>
|
|
128
135
|
<tr>
|
|
136
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ymek"><img src="https://avatars.githubusercontent.com/u/300569?v=4" width="100px;" alt=""/><br /><sub><b>Myke Stubbs</b></sub></a><br /><a href="#code-ymek" title="Code">💻</a> <a href="#bug-ymek" title="Bug reports">🐛</a></td>
|
|
137
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JSap0914"><img src="https://avatars.githubusercontent.com/u/116227558?v=4" width="100px;" alt=""/><br /><sub><b>JSap0914</b></sub></a><br /><a href="#code-JSap0914" title="Code">💻</a></td>
|
|
138
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/knthmn"><img src="https://avatars.githubusercontent.com/u/60438008?v=4" width="100px;" alt=""/><br /><sub><b>Kent Hou Man</b></sub></a><br /><a href="#code-knthmn" title="Code">💻</a> <a href="#bug-knthmn" title="Bug reports">🐛</a></td>
|
|
139
|
+
<td align="center" valign="top" width="14.28%"><a href="https://neumie.dev/"><img src="https://avatars.githubusercontent.com/u/23665482?v=4" width="100px;" alt=""/><br /><sub><b>Jakub Neumann</b></sub></a><br /><a href="#code-neumie" title="Code">💻</a></td>
|
|
140
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cy7372"><img src="https://avatars.githubusercontent.com/u/142197117?v=4" width="100px;" alt=""/><br /><sub><b>CyYu</b></sub></a><br /><a href="#code-cy7372" title="Code">💻</a></td>
|
|
141
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/carolitascl"><img src="https://avatars.githubusercontent.com/u/26188349?v=4" width="100px;" alt=""/><br /><sub><b>Carolina</b></sub></a><br /><a href="#bug-carolitascl" title="Bug reports">🐛</a></td>
|
|
129
142
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/leakedby"><img src="https://avatars.githubusercontent.com/u/4213260?v=4" width="100px;" alt=""/><br /><sub><b>LeakedBy</b></sub></a><br /><a href="#bug-leakedby" title="Bug reports">🐛</a></td>
|
|
143
|
+
</tr>
|
|
144
|
+
<tr>
|
|
130
145
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mdbooth"><img src="https://avatars.githubusercontent.com/u/1318691?v=4" width="100px;" alt=""/><br /><sub><b>Matthew Booth</b></sub></a><br /><a href="#bug-mdbooth" title="Bug reports">🐛</a></td>
|
|
131
146
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Wiedzmin89"><img src="https://avatars.githubusercontent.com/u/61706855?v=4" width="100px;" alt=""/><br /><sub><b>Wiedzmin89</b></sub></a><br /><a href="#ideas-Wiedzmin89" title="Ideas & Planning">🤔</a></td>
|
|
132
147
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/grodingo"><img src="https://avatars.githubusercontent.com/u/244184972?v=4" width="100px;" alt=""/><br /><sub><b>Virgile</b></sub></a><br /><a href="#bug-grodingo" title="Bug reports">🐛</a></td>
|
|
133
148
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/finnvyrn"><img src="https://avatars.githubusercontent.com/u/90801772?v=4" width="100px;" alt=""/><br /><sub><b>Finn</b></sub></a><br /><a href="#ideas-finnvyrn" title="Ideas & Planning">🤔</a></td>
|
|
134
149
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ywh555hhh"><img src="https://avatars.githubusercontent.com/u/121592812?v=4" width="100px;" alt=""/><br /><sub><b>Wayne E</b></sub></a><br /><a href="#bug-ywh555hhh" title="Bug reports">🐛</a></td>
|
|
135
150
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RimuruW"><img src="https://avatars.githubusercontent.com/u/59136309?v=4" width="100px;" alt=""/><br /><sub><b>RimuruW</b></sub></a><br /><a href="#ideas-RimuruW" title="Ideas & Planning">🤔</a></td>
|
|
151
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Bjynt"><img src="https://avatars.githubusercontent.com/u/22177300?v=4" width="100px;" alt=""/><br /><sub><b>Bjynt</b></sub></a><br /><a href="#bug-Bjynt" title="Bug reports">🐛</a> <a href="#ideas-Bjynt" title="Ideas & Planning">🤔</a></td>
|
|
136
152
|
</tr>
|
|
137
153
|
<tr>
|
|
138
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Bjynt"><img src="https://avatars.githubusercontent.com/u/22177300?v=4" width="100px;" alt=""/><br /><sub><b>Bjynt</b></sub></a><br /><a href="#bug-Bjynt" title="Bug reports">🐛</a> <a href="#ideas-Bjynt" title="Ideas & Planning">🤔</a></td>
|
|
139
154
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pvtri96"><img src="https://avatars.githubusercontent.com/u/28696888?v=4" width="100px;" alt=""/><br /><sub><b>Tri Van Pham</b></sub></a><br /><a href="#bug-pvtri96" title="Bug reports">🐛</a></td>
|
|
140
155
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BingWuJ"><img src="https://avatars.githubusercontent.com/u/117666511?v=4" width="100px;" alt=""/><br /><sub><b>BingWuJ</b></sub></a><br /><a href="#bug-BingWuJ" title="Bug reports">🐛</a></td>
|
|
141
156
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tackleberrybey"><img src="https://avatars.githubusercontent.com/u/156954032?v=4" width="100px;" alt=""/><br /><sub><b>tackleberrybey</b></sub></a><br /><a href="#bug-tackleberrybey" title="Bug reports">🐛</a></td>
|
|
142
157
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/TaterDoge"><img src="https://avatars.githubusercontent.com/u/121467933?v=4" width="100px;" alt=""/><br /><sub><b>Mariann Abshire</b></sub></a><br /><a href="#bug-TaterDoge" title="Bug reports">🐛</a></td>
|
|
143
158
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ProbabilityEngineer"><img src="https://avatars.githubusercontent.com/u/38498804?v=4" width="100px;" alt=""/><br /><sub><b>ProbabilityEngineer</b></sub></a><br /><a href="#ideas-ProbabilityEngineer" title="Ideas & Planning">🤔</a></td>
|
|
144
159
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LeonardoRick"><img src="https://avatars.githubusercontent.com/u/17517057?v=4" width="100px;" alt=""/><br /><sub><b>Leonardo Rick</b></sub></a><br /><a href="#bug-LeonardoRick" title="Bug reports">🐛</a> <a href="#ideas-LeonardoRick" title="Ideas & Planning">🤔</a></td>
|
|
160
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kenbanks-peng"><img src="https://avatars.githubusercontent.com/u/26904200?v=4" width="100px;" alt=""/><br /><sub><b>Ken Banks</b></sub></a><br /><a href="#bug-kenbanks-peng" title="Bug reports">🐛</a></td>
|
|
145
161
|
</tr>
|
|
146
162
|
<tr>
|
|
147
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kenbanks-peng"><img src="https://avatars.githubusercontent.com/u/26904200?v=4" width="100px;" alt=""/><br /><sub><b>Ken Banks</b></sub></a><br /><a href="#bug-kenbanks-peng" title="Bug reports">🐛</a></td>
|
|
148
163
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rstacruz"><img src="https://avatars.githubusercontent.com/u/74385?v=4" width="100px;" alt=""/><br /><sub><b>Rico Sta. Cruz</b></sub></a><br /><a href="#ideas-rstacruz" title="Ideas & Planning">🤔</a></td>
|
|
149
164
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/acidnik"><img src="https://avatars.githubusercontent.com/u/1227955?v=4" width="100px;" alt=""/><br /><sub><b>Nikita Bilous</b></sub></a><br /><a href="#bug-acidnik" title="Bug reports">🐛</a></td>
|
|
150
165
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/av1155"><img src="https://avatars.githubusercontent.com/u/117413846?v=4" width="100px;" alt=""/><br /><sub><b>Andrea Arturo Venti Fuentes</b></sub></a><br /><a href="#bug-av1155" title="Bug reports">🐛</a></td>
|
|
151
166
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dacec354"><img src="https://avatars.githubusercontent.com/u/90093629?v=4" width="100px;" alt=""/><br /><sub><b>dacec354</b></sub></a><br /><a href="#bug-dacec354" title="Bug reports">🐛</a></td>
|
|
152
167
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nodnarbnitram"><img src="https://avatars.githubusercontent.com/u/44812862?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Martin</b></sub></a><br /><a href="#bug-nodnarbnitram" title="Bug reports">🐛</a></td>
|
|
153
168
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alpertarhan"><img src="https://avatars.githubusercontent.com/u/50966980?v=4" width="100px;" alt=""/><br /><sub><b>Alper Tarhan</b></sub></a><br /><a href="#bug-alpertarhan" title="Bug reports">🐛</a></td>
|
|
169
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asolopovas"><img src="https://avatars.githubusercontent.com/u/6893216?v=4" width="100px;" alt=""/><br /><sub><b>Andrius Solopovas</b></sub></a><br /><a href="#bug-asolopovas" title="Bug reports">🐛</a></td>
|
|
154
170
|
</tr>
|
|
155
171
|
<tr>
|
|
156
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asolopovas"><img src="https://avatars.githubusercontent.com/u/6893216?v=4" width="100px;" alt=""/><br /><sub><b>Andrius Solopovas</b></sub></a><br /><a href="#bug-asolopovas" title="Bug reports">🐛</a></td>
|
|
157
172
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/GrahamJenkins"><img src="https://avatars.githubusercontent.com/u/6607975?v=4" width="100px;" alt=""/><br /><sub><b>Graham Jenkins</b></sub></a><br /><a href="#bug-GrahamJenkins" title="Bug reports">🐛</a></td>
|
|
158
173
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wings1848"><img src="https://avatars.githubusercontent.com/u/120104016?v=4" width="100px;" alt=""/><br /><sub><b>Wings Butterfly</b></sub></a><br /><a href="#bug-wings1848" title="Bug reports">🐛</a></td>
|
|
159
174
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JohannesKlauss"><img src="https://avatars.githubusercontent.com/u/6214415?v=4" width="100px;" alt=""/><br /><sub><b>Johannes Klauss</b></sub></a><br /><a href="#ideas-JohannesKlauss" title="Ideas & Planning">🤔</a></td>
|
|
160
175
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tan-yong-sheng"><img src="https://avatars.githubusercontent.com/u/64836390?v=4" width="100px;" alt=""/><br /><sub><b>Tan Yong Sheng</b></sub></a><br /><a href="#ideas-tan-yong-sheng" title="Ideas & Planning">🤔</a></td>
|
|
161
176
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fmatray"><img src="https://avatars.githubusercontent.com/u/8267716?v=4" width="100px;" alt=""/><br /><sub><b>Frédéric</b></sub></a><br /><a href="#bug-fmatray" title="Bug reports">🐛</a></td>
|
|
162
177
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fuentesjr"><img src="https://avatars.githubusercontent.com/u/9240?v=4" width="100px;" alt=""/><br /><sub><b>Salvador Fuentes Jr</b></sub></a><br /><a href="#bug-fuentesjr" title="Bug reports">🐛</a></td>
|
|
178
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Viterkim"><img src="https://avatars.githubusercontent.com/u/17838985?v=4" width="100px;" alt=""/><br /><sub><b>Viktor</b></sub></a><br /><a href="#bug-Viterkim" title="Bug reports">🐛</a></td>
|
|
163
179
|
</tr>
|
|
164
180
|
<tr>
|
|
165
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Viterkim"><img src="https://avatars.githubusercontent.com/u/17838985?v=4" width="100px;" alt=""/><br /><sub><b>Viktor</b></sub></a><br /><a href="#bug-Viterkim" title="Bug reports">🐛</a></td>
|
|
166
181
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ortonomy"><img src="https://avatars.githubusercontent.com/u/6688676?v=4" width="100px;" alt=""/><br /><sub><b>Gregory Orton</b></sub></a><br /><a href="#bug-ortonomy" title="Bug reports">🐛</a></td>
|
|
167
182
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jimallen"><img src="https://avatars.githubusercontent.com/u/868773?v=4" width="100px;" alt=""/><br /><sub><b>Jim Allen</b></sub></a><br /><a href="#bug-jimallen" title="Bug reports">🐛</a></td>
|
|
168
183
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/quantfiction"><img src="https://avatars.githubusercontent.com/u/49965454?v=4" width="100px;" alt=""/><br /><sub><b>quantfiction</b></sub></a><br /><a href="#bug-quantfiction" title="Bug reports">🐛</a></td>
|
|
169
184
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Artawower"><img src="https://avatars.githubusercontent.com/u/16963833?v=4" width="100px;" alt=""/><br /><sub><b>Art</b></sub></a><br /><a href="#bug-Artawower" title="Bug reports">🐛</a></td>
|
|
170
185
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chozandrias76"><img src="https://avatars.githubusercontent.com/u/2087677?v=4" width="100px;" alt=""/><br /><sub><b>Colin Swenson-Healey</b></sub></a><br /><a href="#bug-chozandrias76" title="Bug reports">🐛</a></td>
|
|
171
186
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JustlyAI"><img src="https://avatars.githubusercontent.com/u/12634140?v=4" width="100px;" alt=""/><br /><sub><b>Laurent Wiesel</b></sub></a><br /><a href="#bug-JustlyAI" title="Bug reports">🐛</a></td>
|
|
187
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/stark-bit"><img src="https://avatars.githubusercontent.com/u/44064758?v=4" width="100px;" alt=""/><br /><sub><b>Rei Starks</b></sub></a><br /><a href="#bug-stark-bit" title="Bug reports">🐛</a></td>
|
|
172
188
|
</tr>
|
|
173
189
|
<tr>
|
|
174
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/stark-bit"><img src="https://avatars.githubusercontent.com/u/44064758?v=4" width="100px;" alt=""/><br /><sub><b>Rei Starks</b></sub></a><br /><a href="#bug-stark-bit" title="Bug reports">🐛</a></td>
|
|
175
190
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/robertoecf"><img src="https://avatars.githubusercontent.com/u/54923863?v=4" width="100px;" alt=""/><br /><sub><b>Roberto Freitas</b></sub></a><br /><a href="#bug-robertoecf" title="Bug reports">🐛</a></td>
|
|
176
191
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tmttodd"><img src="https://avatars.githubusercontent.com/u/160077416?v=4" width="100px;" alt=""/><br /><sub><b>tmttodd</b></sub></a><br /><a href="#bug-tmttodd" title="Bug reports">🐛</a></td>
|
|
177
192
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/smaharnav"><img src="https://avatars.githubusercontent.com/u/263328627?v=4" width="100px;" alt=""/><br /><sub><b>smaharnav</b></sub></a><br /><a href="#bug-smaharnav" title="Bug reports">🐛</a></td>
|
|
178
193
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MzaGuille"><img src="https://avatars.githubusercontent.com/u/151482698?v=4" width="100px;" alt=""/><br /><sub><b>Mza.Guille</b></sub></a><br /><a href="#bug-MzaGuille" title="Bug reports">🐛</a></td>
|
|
179
194
|
<td align="center" valign="top" width="14.28%"><a href="http://jasonrimmer.com/"><img src="https://avatars.githubusercontent.com/u/629?v=4" width="100px;" alt=""/><br /><sub><b>Jason Rimmer</b></sub></a><br /><a href="#bug-jrimmer" title="Bug reports">🐛</a></td>
|
|
180
195
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ArthurHeymans"><img src="https://avatars.githubusercontent.com/u/15137817?v=4" width="100px;" alt=""/><br /><sub><b>Arthur Heymans</b></sub></a><br /><a href="#ideas-ArthurHeymans" title="Ideas & Planning">🤔</a></td>
|
|
196
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Blue-B"><img src="https://avatars.githubusercontent.com/u/55532956?v=4" width="100px;" alt=""/><br /><sub><b>Blue-B</b></sub></a><br /><a href="#bug-Blue-B" title="Bug reports">🐛</a></td>
|
|
181
197
|
</tr>
|
|
182
198
|
<tr>
|
|
183
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Blue-B"><img src="https://avatars.githubusercontent.com/u/55532956?v=4" width="100px;" alt=""/><br /><sub><b>Blue-B</b></sub></a><br /><a href="#bug-Blue-B" title="Bug reports">🐛</a></td>
|
|
184
199
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bisko"><img src="https://avatars.githubusercontent.com/u/184938?v=4" width="100px;" alt=""/><br /><sub><b>Biser Perchinkov</b></sub></a><br /><a href="#bug-bisko" title="Bug reports">🐛</a></td>
|
|
185
200
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xificurC"><img src="https://avatars.githubusercontent.com/u/4800719?v=4" width="100px;" alt=""/><br /><sub><b>Peter Nagy</b></sub></a><br /><a href="#bug-xificurC" title="Bug reports">🐛</a></td>
|
|
186
201
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/orest-tokovenko-block"><img src="https://avatars.githubusercontent.com/u/190554467?v=4" width="100px;" alt=""/><br /><sub><b>Orest Tokovenko</b></sub></a><br /><a href="#bug-orest-tokovenko-block" title="Bug reports">🐛</a></td>
|
|
187
202
|
<td align="center" valign="top" width="14.28%"><a href="https://eisterman.dev/"><img src="https://avatars.githubusercontent.com/u/3028953?v=4" width="100px;" alt=""/><br /><sub><b>Federico Pasqua</b></sub></a><br /><a href="#bug-eisterman" title="Bug reports">🐛</a></td>
|
|
188
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ymek"><img src="https://avatars.githubusercontent.com/u/300569?v=4" width="100px;" alt=""/><br /><sub><b>Myke Stubbs</b></sub></a><br /><a href="#code-ymek" title="Code">💻</a> <a href="#bug-ymek" title="Bug reports">🐛</a></td>
|
|
189
203
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/black-snow"><img src="https://avatars.githubusercontent.com/u/579733?v=4" width="100px;" alt=""/><br /><sub><b>Ronald</b></sub></a><br /><a href="#bug-black-snow" title="Bug reports">🐛</a></td>
|
|
190
|
-
</tr>
|
|
191
|
-
<tr>
|
|
192
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JSap0914"><img src="https://avatars.githubusercontent.com/u/116227558?v=4" width="100px;" alt=""/><br /><sub><b>JSap0914</b></sub></a><br /><a href="#code-JSap0914" title="Code">💻</a></td>
|
|
193
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AngriestBird"><img src="https://avatars.githubusercontent.com/u/12663269?v=4" width="100px;" alt=""/><br /><sub><b>Kenneth McCormick</b></sub></a><br /><a href="#bug-AngriestBird" title="Bug reports">🐛</a></td>
|
|
194
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/knthmn"><img src="https://avatars.githubusercontent.com/u/60438008?v=4" width="100px;" alt=""/><br /><sub><b>Kent Hou Man</b></sub></a><br /><a href="#code-knthmn" title="Code">💻</a> <a href="#bug-knthmn" title="Bug reports">🐛</a></td>
|
|
195
204
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aaronkyriesenbach"><img src="https://avatars.githubusercontent.com/u/12665860?v=4" width="100px;" alt=""/><br /><sub><b>Aaron Ky-Riesenbach</b></sub></a><br /><a href="#bug-aaronkyriesenbach" title="Bug reports">🐛</a></td>
|
|
196
205
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LovelyLoong"><img src="https://avatars.githubusercontent.com/u/54889641?v=4" width="100px;" alt=""/><br /><sub><b>可爱的龙</b></sub></a><br /><a href="#bug-LovelyLoong" title="Bug reports">🐛</a></td>
|
|
206
|
+
</tr>
|
|
207
|
+
<tr>
|
|
197
208
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pppobear"><img src="https://avatars.githubusercontent.com/u/21952175?v=4" width="100px;" alt=""/><br /><sub><b>pppobear</b></sub></a><br /><a href="#bug-pppobear" title="Bug reports">🐛</a></td>
|
|
198
|
-
<td align="center" valign="top" width="14.28%"><a href="https://neumie.dev/"><img src="https://avatars.githubusercontent.com/u/23665482?v=4" width="100px;" alt=""/><br /><sub><b>Jakub Neumann</b></sub></a><br /><a href="#code-neumie" title="Code">💻</a></td>
|
|
199
209
|
</tr>
|
|
200
210
|
</tbody>
|
|
201
211
|
</table>
|
|
@@ -205,4 +215,10 @@ Thanks goes to these wonderful people:
|
|
|
205
215
|
|
|
206
216
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
207
217
|
|
|
218
|
+
The following commit identities also appear in `git shortlog -sne HEAD` but are
|
|
219
|
+
not represented in the generated table above: Anas Alsbei, Claude,
|
|
220
|
+
Christopher Patti,
|
|
221
|
+
dependabot[bot], Fabio-D, github-actions[bot], JSup, Kenny McCormick,
|
|
222
|
+
Max Lupus, Moritz Hofmann, ricardo, and root.
|
|
223
|
+
|
|
208
224
|
If you land a pull request or report an issue that gets fixed, we'll add you here.
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
import * as fs from "node:fs";
|
|
32
32
|
/**
|
|
33
|
-
* Synchronous atomic write
|
|
33
|
+
* Synchronous atomic write of text or binary data to `${targetPath}.tmp-${pid}`,
|
|
34
34
|
* then `fs.renameSync` over `targetPath`. On any failure (from either step),
|
|
35
35
|
* attempts a best-effort `fs.rmSync(tmp, { force: true })` cleanup, then
|
|
36
36
|
* either swallows (default) or rethrows per `options.bestEffort`.
|
|
@@ -43,7 +43,7 @@ export function writeFileAtomic(targetPath, data, options) {
|
|
|
43
43
|
const bestEffort = options?.bestEffort ?? true;
|
|
44
44
|
const tmpPath = `${targetPath}.tmp-${process.pid}`;
|
|
45
45
|
try {
|
|
46
|
-
fs.writeFileSync(tmpPath, data, "utf-8");
|
|
46
|
+
fs.writeFileSync(tmpPath, data, typeof data === "string" ? "utf-8" : undefined);
|
|
47
47
|
fs.renameSync(tmpPath, targetPath);
|
|
48
48
|
}
|
|
49
49
|
catch (err) {
|
|
@@ -67,7 +67,7 @@ export async function writeFileAtomicAsync(targetPath, data, options) {
|
|
|
67
67
|
const bestEffort = options?.bestEffort ?? true;
|
|
68
68
|
const tmpPath = `${targetPath}.tmp-${process.pid}`;
|
|
69
69
|
try {
|
|
70
|
-
await fs.promises.writeFile(tmpPath, data, "utf-8");
|
|
70
|
+
await fs.promises.writeFile(tmpPath, data, typeof data === "string" ? "utf-8" : undefined);
|
|
71
71
|
await fs.promises.rename(tmpPath, targetPath);
|
|
72
72
|
}
|
|
73
73
|
catch (err) {
|
|
@@ -36,8 +36,12 @@ import * as path from "node:path";
|
|
|
36
36
|
import { isTestMode } from "./env-utils.js";
|
|
37
37
|
import { getGlobalPiLensDir } from "./file-utils.js";
|
|
38
38
|
import { createNdjsonLogger } from "./ndjson-logger.js";
|
|
39
|
+
import { getMaxLogSizeMB } from "./log-cleanup.js";
|
|
39
40
|
const BUS_EVENTS_LOG_FILE = path.join(getGlobalPiLensDir(), "bus-events.log");
|
|
40
|
-
const writer = createNdjsonLogger({
|
|
41
|
+
const writer = createNdjsonLogger({
|
|
42
|
+
filePath: BUS_EVENTS_LOG_FILE,
|
|
43
|
+
maxBytes: getMaxLogSizeMB() * 1024 * 1024,
|
|
44
|
+
});
|
|
41
45
|
export function logBusEvent(entry) {
|
|
42
46
|
if (isTestMode()) {
|
|
43
47
|
return;
|
|
@@ -9,11 +9,24 @@ import * as fs from "node:fs";
|
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import { getProjectDataDir } from "../file-utils.js";
|
|
11
11
|
import { readJsonCache } from "../json-cache-read.js";
|
|
12
|
-
|
|
12
|
+
// v4: cache skip_test_files + fix_action — v3 entries silently dropped them,
|
|
13
|
+
// and ruleHash (rule-file mtimes) never invalidates on a code-only fix.
|
|
14
|
+
// v5 (#675): rule SELECTION changed in code — javascript no longer inherits the
|
|
15
|
+
// typescript rule set and tsx now does. A v4 entry holds the old merge, and
|
|
16
|
+
// queries now compile against the file's parse language, so replaying it would
|
|
17
|
+
// fire typescript rules on javascript trees for real this time.
|
|
18
|
+
// v6 (#878): the ruleHash FINGERPRINT now covers the full effective rule set —
|
|
19
|
+
// inherited rule-source directories (tsx also runs typescript rules), not just
|
|
20
|
+
// the language's own. A v5 tsx entry was hashed over tsx files only, so a
|
|
21
|
+
// typescript-rule edit never invalidated it; v5 entries self-miss anyway (the
|
|
22
|
+
// hash input set changed), the bump just makes the semantics break explicit.
|
|
23
|
+
export const CACHE_VERSION = "v6";
|
|
13
24
|
export class RuleCache {
|
|
14
25
|
cacheFile;
|
|
15
26
|
cacheDir;
|
|
27
|
+
language;
|
|
16
28
|
constructor(language, rootDir = process.cwd()) {
|
|
29
|
+
this.language = language;
|
|
17
30
|
this.cacheDir = path.join(getProjectDataDir(rootDir), "cache");
|
|
18
31
|
this.cacheFile = path.join(this.cacheDir, `${language}-rules-${CACHE_VERSION}.json`);
|
|
19
32
|
}
|
|
@@ -40,7 +53,8 @@ export class RuleCache {
|
|
|
40
53
|
const currentHash = this.computeRuleHash(ruleFiles);
|
|
41
54
|
const cached = readJsonCache(this.cacheFile, (parsed) => {
|
|
42
55
|
const entry = parsed;
|
|
43
|
-
if (entry.version !== CACHE_VERSION ||
|
|
56
|
+
if (entry.version !== CACHE_VERSION ||
|
|
57
|
+
entry.ruleHash !== currentHash) {
|
|
44
58
|
return undefined; // Cache invalid
|
|
45
59
|
}
|
|
46
60
|
return entry;
|
|
@@ -61,11 +75,38 @@ export class RuleCache {
|
|
|
61
75
|
queries,
|
|
62
76
|
};
|
|
63
77
|
fs.writeFileSync(this.cacheFile, JSON.stringify(entry, null, 2));
|
|
78
|
+
this.pruneStaleVersions();
|
|
64
79
|
}
|
|
65
80
|
catch {
|
|
66
81
|
// Cache write failure is non-fatal
|
|
67
82
|
}
|
|
68
83
|
}
|
|
84
|
+
// Orphaned `<language>-rules-v<N>.json` files from a prior CACHE_VERSION
|
|
85
|
+
// (e.g. v3 entries left behind by the #448 v3→v4 bump) never got cleaned up
|
|
86
|
+
// on their own — nothing ever read or removed them again once the version
|
|
87
|
+
// bumped. Delete every sibling for this language that isn't the current file.
|
|
88
|
+
pruneStaleVersions() {
|
|
89
|
+
const currentName = path.basename(this.cacheFile);
|
|
90
|
+
const pattern = new RegExp(`^${this.language}-rules-v\\d+\\.json$`);
|
|
91
|
+
let dirents;
|
|
92
|
+
try {
|
|
93
|
+
dirents = fs.readdirSync(this.cacheDir);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const name of dirents) {
|
|
99
|
+
if (name === currentName || !pattern.test(name))
|
|
100
|
+
continue;
|
|
101
|
+
try {
|
|
102
|
+
fs.unlinkSync(path.join(this.cacheDir, name));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Best-effort; ENOENT (already gone) or any other removal failure
|
|
106
|
+
// shouldn't undo the write that already succeeded above.
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
69
110
|
clear() {
|
|
70
111
|
try {
|
|
71
112
|
if (fs.existsSync(this.cacheFile)) {
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt-cache observability (#1018).
|
|
3
|
+
*
|
|
4
|
+
* Two provider-independent signals, both sinking into ~/.pi-lens/latency.log
|
|
5
|
+
* via {@link logLatency} (matching the neighboring `type: "phase"` records):
|
|
6
|
+
*
|
|
7
|
+
* 1. Response-side cache usage (`cache_usage`) — on each assistant
|
|
8
|
+
* `message_end`, record the provider-reported token/cost breakdown so a
|
|
9
|
+
* session's actual cacheRead/cacheWrite behavior is queryable after the
|
|
10
|
+
* fact. This is what the provider DID, not what we hoped it would do.
|
|
11
|
+
*
|
|
12
|
+
* 2. Request-side prefix stability (`cache_prefix_break`) — a content hash of
|
|
13
|
+
* `messages[0]` observed on every `context` call. After #1016 the first
|
|
14
|
+
* message must stay byte-stable across a whole session; a logged CHANGE
|
|
15
|
+
* flags that something (pi-lens or otherwise) broke the cache prefix, which
|
|
16
|
+
* would silently invalidate the entire prompt-cache on every prefix-caching
|
|
17
|
+
* provider. Pure observation — it never alters context injection.
|
|
18
|
+
*
|
|
19
|
+
* Both paths are defensive: `usage` (and its fields) may be absent on older
|
|
20
|
+
* hosts or non-assistant messages, so every access is guarded and the handlers
|
|
21
|
+
* never throw — on error they `dbg(...)` and no-op, like the other index.ts
|
|
22
|
+
* event handlers.
|
|
23
|
+
*/
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import { logLatency } from "./latency-logger.js";
|
|
26
|
+
/**
|
|
27
|
+
* Part 1 — log one `cache_usage` record for an assistant `message_end` that
|
|
28
|
+
* carries a `usage`. Provider/model come straight off the message itself
|
|
29
|
+
* (`AssistantMessage.provider` / `.model` in pi-ai) — no dependency on the
|
|
30
|
+
* runtime telemetry identity. Skips silently (no record) when the message is
|
|
31
|
+
* not an assistant message or has no usage; never logs a zeros-only record for
|
|
32
|
+
* a message that simply lacks usage.
|
|
33
|
+
*/
|
|
34
|
+
export function logCacheUsage(message, dbg) {
|
|
35
|
+
try {
|
|
36
|
+
if (!message || typeof message !== "object")
|
|
37
|
+
return;
|
|
38
|
+
const msg = message;
|
|
39
|
+
// Only assistant messages carry LLM usage; tool-result / user messages
|
|
40
|
+
// (and unknown custom AgentMessage variants) are skipped.
|
|
41
|
+
if (msg.role !== "assistant")
|
|
42
|
+
return;
|
|
43
|
+
const usage = msg.usage;
|
|
44
|
+
if (!usage || typeof usage !== "object")
|
|
45
|
+
return;
|
|
46
|
+
const u = usage;
|
|
47
|
+
logLatency({
|
|
48
|
+
type: "phase",
|
|
49
|
+
filePath: "<pi-lens>",
|
|
50
|
+
phase: "cache_usage",
|
|
51
|
+
durationMs: 0,
|
|
52
|
+
metadata: {
|
|
53
|
+
provider: typeof msg.provider === "string" ? msg.provider : undefined,
|
|
54
|
+
model: typeof msg.model === "string" ? msg.model : undefined,
|
|
55
|
+
cacheRead: u.cacheRead,
|
|
56
|
+
cacheWrite: u.cacheWrite,
|
|
57
|
+
input: u.input,
|
|
58
|
+
output: u.output,
|
|
59
|
+
// `Usage.cost` is a breakdown object; the total is the headline number.
|
|
60
|
+
cost: u.cost?.total,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
dbg?.(`cache-usage: failed to log message_end usage: ${err}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Content hash of the first transcript message. Stable for identical content
|
|
70
|
+
* (role + content are serialized in a fixed order), so a changing hash means
|
|
71
|
+
* `messages[0]` actually changed byte-for-byte.
|
|
72
|
+
*/
|
|
73
|
+
function hashFirstMessage(first) {
|
|
74
|
+
const serialized = JSON.stringify({ role: first.role, content: first.content });
|
|
75
|
+
return createHash("sha256").update(serialized).digest("hex");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Per-session baseline hash of `messages[0]`, keyed by the STABLE pi session id
|
|
79
|
+
* (`ctx.sessionManager.getSessionId()`). A single module-scoped var was wrong:
|
|
80
|
+
* in ONE process, multiple logical conversations touch this module, and they
|
|
81
|
+
* must not share a baseline —
|
|
82
|
+
* - new / fork / a concurrent in-process subagent (#473) is a DIFFERENT id →
|
|
83
|
+
* its OWN independent baseline, never compared against another id's (else a
|
|
84
|
+
* benign session boundary logs a spurious `cache_prefix_break`, and a
|
|
85
|
+
* concurrent subagent + its parent stomp each other's baseline, each
|
|
86
|
+
* emitting a false positive — fatal for a signal whose value is trust);
|
|
87
|
+
* - an IN-PROCESS reload / resume reuses the SAME id → the baseline is still in
|
|
88
|
+
* this map, so a genuine break is caught (no blinding reset). NOTE: a full
|
|
89
|
+
* process restart (quit → `pi --session <id>`) starts a NEW process with an
|
|
90
|
+
* empty map, so the first post-restart `context` re-anchors a fresh baseline
|
|
91
|
+
* rather than comparing across the restart. That's acceptable here — this is a
|
|
92
|
+
* pure observability signal (at worst a missed `cache_prefix_break` log on the
|
|
93
|
+
* first post-restart turn, never a user-facing action) — unlike genuine
|
|
94
|
+
* session state (read guard #1041, widget #190) which IS rehydrated from the
|
|
95
|
+
* sidecar.
|
|
96
|
+
*
|
|
97
|
+
* Bounded as an insertion-ordered LRU (evict oldest-inserted past the cap) so a
|
|
98
|
+
* long-lived process cycling through many sessions can't grow this unbounded.
|
|
99
|
+
*/
|
|
100
|
+
const MAX_TRACKED_SESSIONS = 32;
|
|
101
|
+
const prefixHashBySession = new Map();
|
|
102
|
+
/**
|
|
103
|
+
* Bucket key used when no session id is available (undefined/empty). Degrades
|
|
104
|
+
* gracefully to the old single-var semantics — one shared baseline — rather than
|
|
105
|
+
* throwing or dropping the signal.
|
|
106
|
+
*/
|
|
107
|
+
const NO_SESSION_KEY = "<no-session>";
|
|
108
|
+
/**
|
|
109
|
+
* Store `hash` for `key`, refreshing its LRU recency (re-insert moves it to the
|
|
110
|
+
* newest position since `Map` preserves insertion order) and evicting the
|
|
111
|
+
* oldest-inserted entries once the cap is exceeded.
|
|
112
|
+
*/
|
|
113
|
+
function recordSessionHash(key, hash) {
|
|
114
|
+
prefixHashBySession.delete(key);
|
|
115
|
+
prefixHashBySession.set(key, hash);
|
|
116
|
+
while (prefixHashBySession.size > MAX_TRACKED_SESSIONS) {
|
|
117
|
+
const oldest = prefixHashBySession.keys().next().value;
|
|
118
|
+
if (oldest === undefined)
|
|
119
|
+
break;
|
|
120
|
+
prefixHashBySession.delete(oldest);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Part 2 — observe `messages[0]` stability turn-over-turn, keyed by session id.
|
|
125
|
+
* Logs a baseline the first time it sees a non-empty transcript FOR A GIVEN
|
|
126
|
+
* session id, then logs a `cache_prefix_break` whenever that same session's
|
|
127
|
+
* first-message hash changes. Pure observation: it never inspects or mutates
|
|
128
|
+
* anything but its own per-session hash map, and never throws.
|
|
129
|
+
*
|
|
130
|
+
* `sessionId` should be pi's STABLE id (`ctx.sessionManager.getSessionId()`),
|
|
131
|
+
* which uniquely identifies the CURRENTLY-firing session: a concurrent
|
|
132
|
+
* in-process subagent runs its own `AgentSession`/`sessionManager`, so its
|
|
133
|
+
* `context` calls carry a DIFFERENT id than the parent's and get their own
|
|
134
|
+
* baseline. (`runtime.telemetrySessionId` cannot be used here: per the #473
|
|
135
|
+
* guard a concurrent secondary skips `updateRuntimeIdentityFromEvent`, so that
|
|
136
|
+
* process-global singleton stays pinned to the PARENT — it would collapse
|
|
137
|
+
* parent and subagent onto one baseline.) Residual limitation: if the host ever
|
|
138
|
+
* does NOT supply a stable id, all such sessions collapse onto `NO_SESSION_KEY`
|
|
139
|
+
* and behave like the old single-var (a concurrent subagent could then still
|
|
140
|
+
* cross-contaminate) — accepted as graceful degradation, not silently perfect.
|
|
141
|
+
*
|
|
142
|
+
* Does nothing on an empty transcript (nothing to anchor a prefix to yet).
|
|
143
|
+
*/
|
|
144
|
+
export function observeCachePrefix(messages, turnIndex, sessionId, sessionRole, dbg) {
|
|
145
|
+
try {
|
|
146
|
+
if (!messages || messages.length === 0)
|
|
147
|
+
return;
|
|
148
|
+
const first = messages[0];
|
|
149
|
+
if (!first || typeof first !== "object")
|
|
150
|
+
return;
|
|
151
|
+
const key = sessionId?.trim() ? sessionId.trim() : NO_SESSION_KEY;
|
|
152
|
+
const currentHash = hashFirstMessage(first);
|
|
153
|
+
const previousHash = prefixHashBySession.get(key);
|
|
154
|
+
if (previousHash === undefined) {
|
|
155
|
+
// Baseline: record this session's starting prefix so a later break has a
|
|
156
|
+
// reference point in the log. `previousHash: null` marks the baseline.
|
|
157
|
+
recordSessionHash(key, currentHash);
|
|
158
|
+
logLatency({
|
|
159
|
+
type: "phase",
|
|
160
|
+
filePath: "<pi-lens>",
|
|
161
|
+
phase: "cache_prefix_break",
|
|
162
|
+
durationMs: 0,
|
|
163
|
+
metadata: {
|
|
164
|
+
turnIndex,
|
|
165
|
+
previousHash: null,
|
|
166
|
+
currentHash,
|
|
167
|
+
baseline: true,
|
|
168
|
+
sessionId: key,
|
|
169
|
+
sessionRole,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (currentHash !== previousHash) {
|
|
175
|
+
logLatency({
|
|
176
|
+
type: "phase",
|
|
177
|
+
filePath: "<pi-lens>",
|
|
178
|
+
phase: "cache_prefix_break",
|
|
179
|
+
durationMs: 0,
|
|
180
|
+
metadata: {
|
|
181
|
+
turnIndex,
|
|
182
|
+
previousHash,
|
|
183
|
+
currentHash,
|
|
184
|
+
sessionId: key,
|
|
185
|
+
sessionRole,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
// Refresh recency (and update the stored hash after a break) so an active
|
|
190
|
+
// session stays warm in the LRU and isn't evicted while still in use.
|
|
191
|
+
recordSessionHash(key, currentHash);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
dbg?.(`cache-prefix: failed to observe messages[0]: ${err}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Drop a single session's prefix baseline. Called from the `session_shutdown`
|
|
199
|
+
* handler (primary path only — the concurrent-secondary guard there returns
|
|
200
|
+
* first, and the LRU cap backstops any secondary entry left behind) so an ended
|
|
201
|
+
* conversation's entry is reclaimed promptly rather than only when the LRU
|
|
202
|
+
* evicts it. Idempotent and never throws.
|
|
203
|
+
*/
|
|
204
|
+
export function clearCachePrefixSession(sessionId) {
|
|
205
|
+
const key = sessionId?.trim() ? sessionId.trim() : NO_SESSION_KEY;
|
|
206
|
+
prefixHashBySession.delete(key);
|
|
207
|
+
}
|
|
208
|
+
/** Clear all per-session prefix hashes. For tests / session boundaries. */
|
|
209
|
+
export function resetCachePrefixObservation() {
|
|
210
|
+
prefixHashBySession.clear();
|
|
211
|
+
}
|