@piagent/platform 1.5.5 → 1.6.1
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 +166 -0
- package/README.md +25 -21
- package/README.vi.md +34 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +92 -0
- package/benchmarks/production-v1/suite.json +46 -3
- package/catalog/capabilities.json +18 -18
- package/docs/authority-manifest.md +1 -1
- package/docs/auto-delegation-policy.md +38 -73
- package/docs/command-reference-vietnamese.md +27 -35
- package/docs/context-engine.md +12 -4
- package/docs/context-window-policy.md +37 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +27 -13
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +27 -45
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +10 -0
- package/docs/quality-benchmark.md +427 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-harness-standard.md +3 -3
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/subagent-orchestration-capabilities.md +19 -51
- package/docs/subagents-and-multiagent.md +21 -47
- package/docs/task-implementation-contract.md +3 -3
- package/docs/task-lifecycle-vietnamese.md +3 -3
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +27 -13
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +89 -95
- package/docs-site/commands.html +1 -1
- package/docs-site/content/benchmark.html +88 -94
- package/docs-site/content/en/benchmark.html +134 -90
- package/docs-site/content/en/index.html +19 -19
- package/docs-site/content/en/runtime.html +4 -4
- package/docs-site/content/en/whats-new.html +12 -1
- package/docs-site/content/en/workflows.html +2 -2
- package/docs-site/content/index.html +19 -19
- package/docs-site/content/runtime.html +4 -4
- package/docs-site/content/whats-new.html +12 -1
- package/docs-site/content/workflows.html +2 -2
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +135 -91
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +20 -20
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +5 -5
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +13 -2
- package/docs-site/en/workflows.html +3 -3
- package/docs-site/index.html +20 -20
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +5 -5
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +13 -2
- package/docs-site/workflows.html +3 -3
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
- package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
- package/evals/fixtures/helper-request.invalid.json +1 -1
- package/evals/fixtures/helper-request.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
- package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v1.json +3 -3
- package/evals/fs5-pilot-protocol.v2.json +8 -8
- package/evals/fs5-pilot-protocol.v3.json +8 -8
- package/evals/fs5-pilot-protocol.v4.json +7 -6
- package/evals/fs5-pilot-protocol.v5.json +9 -7
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/evals/runtime-conformance-v1/README.md +43 -0
- package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
- package/evals/runtime-conformance-v1/lane.json +57 -0
- package/evals/runtime-conformance-v1/paid-canary.json +31 -0
- package/evals/runtime-conformance-v1/runner.mjs +456 -0
- package/package.json +3 -1
- package/packages/piagent-core/README.md +13 -4
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +736 -664
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +441 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +5 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +252 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +130 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +979 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +149 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +380 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +322 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +123 -59
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +149 -156
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-index-policy.js +4 -0
- package/packages/piagent-core/extensions/context-query-paths.js +57 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +435 -7
- package/packages/piagent-core/extensions/criterion-graph.js +82 -9
- package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
- package/packages/piagent-core/extensions/guard-types.ts +6 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +420 -260
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +39 -2
- package/packages/piagent-core/extensions/task-state.js +109 -142
- package/packages/piagent-core/extensions/verification-intelligence.js +52 -13
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +193 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +44 -2
- package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
- package/packages/piagent-core/prompts/be-to-fe.md +2 -8
- package/packages/piagent-core/prompts/plan.md +1 -1
- package/packages/piagent-core/prompts/platform-improve.md +2 -6
- package/packages/piagent-core/prompts/review.md +2 -7
- package/packages/piagent-core/prompts/scout.md +1 -1
- package/packages/piagent-core/prompts/task.md +4 -3
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +12 -12
- package/packages/piagent-core/runtime/hooks/input-hook.ts +17 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +107 -3
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +134 -166
- package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
- package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +70 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +73 -9
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
- package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
- package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
- package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
- package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +7 -2
- package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +22 -11
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +9 -5
- package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
- package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
- package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
- package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +60 -13
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
- package/packages/piagent-core/runtime/registration/context-commands.ts +82 -7
- package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
- package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
- package/packages/piagent-core/runtime/registration/policy-tools.ts +50 -15
- package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +61 -25
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +169 -0
- package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +108 -103
- package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
- package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
- package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
- package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
- package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
- package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
- package/packages/piagent-core/runtime/session/runtime-state.ts +161 -5
- package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
- package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
- package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
- package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
- package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
- package/packages/piagent-core/runtime/workflows/task-intake.ts +207 -38
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
- package/packages/piagent-core/subagents/piagent-planner.md +6 -4
- package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
- package/packages/piagent-core/subagents/piagent-scout.md +4 -2
- package/packages/piagent-core/subagents/piagent-worker.md +7 -5
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-B4XehTCU.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-CgOEyRPG.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
- package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
- package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +122 -20
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
- package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +165 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +141 -125
- package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
- package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +113 -9
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
- package/schemas/project-profile.schema.json +1 -1
- package/schemas/task-contract.schema.json +27 -1
- package/scripts/benchmark-runner-core.mjs +367 -363
- package/scripts/benchmark-runner-finalization.mjs +313 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/configure-subagents.sh +42 -34
- package/scripts/helper-evaluation.mjs +2 -2
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/setup.sh +2 -2
- package/scripts/team-doctor.sh +1 -1
- package/scripts/verify-local.sh +10 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -2,7 +2,6 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
|
|
6
5
|
import {
|
|
7
6
|
contextIndexExcludeDigest,
|
|
8
7
|
contextIndexExcludePolicyVersion,
|
|
@@ -11,9 +10,14 @@ import {
|
|
|
11
10
|
import { matchesAnyPath, matchesProtectedPath } from "./policy-core.js";
|
|
12
11
|
import { appendJsonlBounded, readJsonlTail } from "./state-retention.js";
|
|
13
12
|
import { ensurePrivateStateDirectory, resolveLocalStatePath } from "./local-state-path.js";
|
|
14
|
-
|
|
13
|
+
import { contextMetricPartition } from "./context-efficiency-metrics.js";
|
|
14
|
+
import { writeContextEfficiencyReport } from "./context-efficiency-report.js";
|
|
15
|
+
import { inspectContextTelemetryIntegrity } from "./context-telemetry-integrity.js";
|
|
16
|
+
import { directExplicitImportLinks, extractJavaScriptModuleImports } from "./context-import-links.js";
|
|
17
|
+
import { queryPathCandidates } from "./context-query-paths.js";
|
|
18
|
+
import { redactSensitiveProjectFileText } from "../security/sensitive-data.js";
|
|
15
19
|
const INDEX_SCHEMA_VERSION = 2;
|
|
16
|
-
const TELEMETRY_SCHEMA_VERSION =
|
|
20
|
+
const TELEMETRY_SCHEMA_VERSION = 2;
|
|
17
21
|
const RRF_K = 60;
|
|
18
22
|
const DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
19
23
|
const DEFAULT_MAX_FILES = 8_000;
|
|
@@ -75,6 +79,10 @@ function clampInteger(value, fallback, min, max) {
|
|
|
75
79
|
return Math.max(min, Math.min(max, Math.trunc(number)));
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
function ratio(numerator, denominator) {
|
|
83
|
+
return denominator > 0 ? numerator / denominator : 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
78
86
|
function requireExplicitExcludePatterns(options, operation) {
|
|
79
87
|
if (!Array.isArray(options?.excludePatterns)) {
|
|
80
88
|
throw new TypeError(`${operation} requires an explicit excludePatterns array`);
|
|
@@ -258,7 +266,7 @@ function isTextBuffer(buffer) {
|
|
|
258
266
|
return sample.length === 0 || suspicious / sample.length < 0.02;
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
function shouldIndexPath(relativePath, options) {
|
|
269
|
+
export function shouldIndexPath(relativePath, options = {}) {
|
|
262
270
|
const rel = normalizeRelative(relativePath);
|
|
263
271
|
if (!rel || path.isAbsolute(rel) || rel.startsWith("../")) return false;
|
|
264
272
|
const segments = rel.split("/");
|
|
@@ -417,12 +425,12 @@ function extractSymbols(text, language) {
|
|
|
417
425
|
return symbols;
|
|
418
426
|
}
|
|
419
427
|
|
|
420
|
-
function extractImports(text, language) {
|
|
428
|
+
function extractImports(text, language, filePath = "") {
|
|
421
429
|
const lines = text.split(/\r?\n/);
|
|
422
430
|
const imports = [];
|
|
423
431
|
const patterns = [];
|
|
424
432
|
if (["typescript", "javascript", "svelte", "vue"].includes(language)) {
|
|
425
|
-
|
|
433
|
+
return extractJavaScriptModuleImports(text, { embedded: ["svelte", "vue"].includes(language), jsx: /\.[jt]sx$/i.test(filePath) });
|
|
426
434
|
} else if (language === "python") {
|
|
427
435
|
patterns.push(/^\s*from\s+([A-Za-z_][\w.]*)\s+import\b/, /^\s*import\s+([A-Za-z_][\w.]*)/);
|
|
428
436
|
} else if (["java", "kotlin", "scala"].includes(language)) {
|
|
@@ -485,17 +493,6 @@ function tokenizeQuery(query) {
|
|
|
485
493
|
return [...new Set(terms.map((term) => term.toLowerCase()).filter((term) => !STOP_TERMS.has(foldSearchSignal(term))))].slice(0, 16);
|
|
486
494
|
}
|
|
487
495
|
|
|
488
|
-
function queryPathCandidates(query) {
|
|
489
|
-
const pattern = /(?:^|[\s"'`(])((?:\.{0,2}\/)?[\p{L}\p{N}_.@-]+(?:\/[\p{L}\p{N}_.@-]+)+|[\p{L}\p{N}_.@-]+\.[\p{L}\p{N}]{1,8}|\.[\p{L}\p{N}_@-]{1,32})(?=$|[\s"'`),:])/gu;
|
|
490
|
-
const explicitRoots = /^(?:\.github|app|apps|backend|bin|config|docs|examples|frontend|lib|logs|packages|pages|public|scripts|services|spec|src|test|tests|vendor|__tests__)\//i;
|
|
491
|
-
return [...String(query ?? "").matchAll(pattern)].flatMap((match) => {
|
|
492
|
-
const raw = match[1], candidate = normalizeRelative(raw);
|
|
493
|
-
const explicit = candidate && (raw.startsWith("./") || raw.startsWith("../") || explicitRoots.test(candidate)
|
|
494
|
-
|| candidate.split("/").length >= 3 || /(?:^|\/)(?:\.[^/]+|[^/]+\.[\p{L}\p{N}]{1,8})$/u.test(candidate));
|
|
495
|
-
return explicit ? [candidate] : [];
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
|
|
499
496
|
function ftsQuery(terms) {
|
|
500
497
|
return terms.map((term) => `"${term.replaceAll('"', '""')}"`).join(" OR ");
|
|
501
498
|
}
|
|
@@ -627,7 +624,7 @@ export async function buildContextIndexV2(cwd, options = {}) {
|
|
|
627
624
|
for (const item of changed) {
|
|
628
625
|
const language = languageForPath(item.relativePath);
|
|
629
626
|
const symbols = extractSymbols(item.text, language);
|
|
630
|
-
const imports = extractImports(item.text, language);
|
|
627
|
+
const imports = extractImports(item.text, language, item.relativePath);
|
|
631
628
|
insertFile.run(item.relativePath, item.contentHash, item.stat.size, item.stat.mtimeMs, language, indexedAt);
|
|
632
629
|
insertFts.run(item.relativePath, item.text, symbols.map((symbol) => symbol.name).join(" "));
|
|
633
630
|
for (const symbol of symbols) {
|
|
@@ -859,10 +856,13 @@ function retrievalFeedback(cwd) {
|
|
|
859
856
|
} catch {
|
|
860
857
|
return {
|
|
861
858
|
rows: [],
|
|
859
|
+
observedSelected: 0,
|
|
862
860
|
selected: 0,
|
|
863
861
|
used: 0,
|
|
864
862
|
unused: 0,
|
|
865
|
-
utilizationRate: 0
|
|
863
|
+
utilizationRate: 0,
|
|
864
|
+
fallbackRereads: 0,
|
|
865
|
+
fallbackRereadRate: 0
|
|
866
866
|
};
|
|
867
867
|
}
|
|
868
868
|
const cached = retrievalFeedbackCache.get(cwd);
|
|
@@ -871,33 +871,61 @@ function retrievalFeedback(cwd) {
|
|
|
871
871
|
const events = readContextTelemetry(cwd, { limit: 12_000, maxBytes: 12 * 1024 * 1024 });
|
|
872
872
|
const selectedCounts = new Map();
|
|
873
873
|
const usedCounts = new Map();
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
874
|
+
const fallbackReadCounts = new Map();
|
|
875
|
+
const latestPackByPartition = new Map();
|
|
876
|
+
const taskByTurn = new Map(events.filter((event) => event.event === "turn_task_bound" && typeof event.sessionId === "string" && typeof event.turnId === "string" && typeof event.taskRunId === "string")
|
|
877
|
+
.map((event) => [`${event.sessionId}\0${event.turnId}`, event.taskRunId]));
|
|
878
|
+
let observedSelected = 0, selected = 0, used = 0, fallbackRereads = 0;
|
|
879
|
+
|
|
880
|
+
const eventPaths = (event) => [...new Set([
|
|
881
|
+
...(Array.isArray(event.selectedPaths) ? event.selectedPaths : []),
|
|
882
|
+
...(Array.isArray(event.selectedItems) ? event.selectedItems.map((item) => item?.path) : [])
|
|
883
|
+
].map(normalizeRelative).filter((filePath) => shouldIndexPath(filePath, {})))];
|
|
884
|
+
const successfulMutationPaths = (event) => {
|
|
885
|
+
if (event.event !== "tool_result" || event.isError === true) return [];
|
|
886
|
+
const explicit = [event.changedPaths, event.mutationPaths, event.targetPaths]
|
|
887
|
+
.find((value) => Array.isArray(value));
|
|
888
|
+
if (explicit) return [...new Set(explicit.map(normalizeRelative).filter(Boolean))];
|
|
889
|
+
// These tools have direct file-mutation semantics. Shell and opaque MCP
|
|
890
|
+
// results are intentionally excluded unless they carry explicit changed
|
|
891
|
+
// paths; a zero exit code alone is not mutation evidence.
|
|
892
|
+
const toolName = String(event.toolName ?? "").toLowerCase();
|
|
893
|
+
if (!["write", "edit", "apply_patch", "patch"].includes(toolName)) return [];
|
|
894
|
+
const targetPath = normalizeRelative(event.targetPath);
|
|
895
|
+
return targetPath ? [targetPath] : [];
|
|
896
|
+
};
|
|
877
897
|
|
|
878
898
|
for (const event of events) {
|
|
879
|
-
const
|
|
880
|
-
if (event.event === "
|
|
881
|
-
const selectedPaths =
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
)
|
|
886
|
-
const pack = { selectedPaths: new Set(selectedPaths), usedPaths: new Set() };
|
|
887
|
-
latestPackBySession.set(sessionId, pack);
|
|
899
|
+
const partition = contextMetricPartition(event, taskByTurn);
|
|
900
|
+
if (event.event === "context_pack_injected") {
|
|
901
|
+
const selectedPaths = eventPaths(event);
|
|
902
|
+
observedSelected += selectedPaths.length;
|
|
903
|
+
if (!partition) continue;
|
|
904
|
+
const pack = { selectedPaths: new Set(selectedPaths), usedPaths: new Set(), fallbackReadPaths: new Set() };
|
|
905
|
+
latestPackByPartition.set(partition, pack);
|
|
888
906
|
for (const filePath of selectedPaths) {
|
|
889
907
|
selectedCounts.set(filePath, (selectedCounts.get(filePath) ?? 0) + 1);
|
|
890
908
|
selected += 1;
|
|
891
909
|
}
|
|
892
910
|
continue;
|
|
893
911
|
}
|
|
894
|
-
if (
|
|
912
|
+
if (!partition) continue;
|
|
913
|
+
const pack = latestPackByPartition.get(partition);
|
|
914
|
+
if (!pack) continue;
|
|
895
915
|
const targetPath = normalizeRelative(event.targetPath);
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
916
|
+
if (event.event === "tool_call" && ["read", "grep", "find", "ls"].includes(event.toolName)
|
|
917
|
+
&& targetPath && pack.selectedPaths.has(targetPath) && !pack.fallbackReadPaths.has(targetPath)) {
|
|
918
|
+
pack.fallbackReadPaths.add(targetPath);
|
|
919
|
+
fallbackReadCounts.set(targetPath, (fallbackReadCounts.get(targetPath) ?? 0) + 1);
|
|
920
|
+
fallbackRereads += 1;
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
for (const mutationPath of successfulMutationPaths(event)) {
|
|
924
|
+
if (!pack.selectedPaths.has(mutationPath) || pack.usedPaths.has(mutationPath)) continue;
|
|
925
|
+
pack.usedPaths.add(mutationPath);
|
|
926
|
+
usedCounts.set(mutationPath, (usedCounts.get(mutationPath) ?? 0) + 1);
|
|
927
|
+
used += 1;
|
|
928
|
+
}
|
|
901
929
|
}
|
|
902
930
|
|
|
903
931
|
const rows = [...usedCounts.entries()]
|
|
@@ -909,16 +937,20 @@ function retrievalFeedback(cwd) {
|
|
|
909
937
|
path: filePath,
|
|
910
938
|
selectedCount,
|
|
911
939
|
usedCount,
|
|
940
|
+
fallbackRereadCount: fallbackReadCounts.get(filePath) ?? 0,
|
|
912
941
|
feedbackScore: utilization * evidence
|
|
913
942
|
};
|
|
914
943
|
})
|
|
915
944
|
.sort((left, right) => right.feedbackScore - left.feedbackScore || right.usedCount - left.usedCount || left.path.localeCompare(right.path));
|
|
916
945
|
const value = {
|
|
917
946
|
rows,
|
|
947
|
+
observedSelected,
|
|
918
948
|
selected,
|
|
919
949
|
used,
|
|
920
950
|
unused: Math.max(0, selected - used),
|
|
921
|
-
utilizationRate: ratio(used, selected)
|
|
951
|
+
utilizationRate: ratio(used, selected),
|
|
952
|
+
fallbackRereads,
|
|
953
|
+
fallbackRereadRate: ratio(fallbackRereads, selected)
|
|
922
954
|
};
|
|
923
955
|
retrievalFeedbackCache.set(cwd, { signature, value });
|
|
924
956
|
return value;
|
|
@@ -941,15 +973,15 @@ export async function searchContextIndexV2(cwd, query, options = {}) {
|
|
|
941
973
|
const allFiles = db.prepare("SELECT path, mtime_ms FROM files").all().filter((file) => pathAllowed(file.path));
|
|
942
974
|
const explicitRows = [];
|
|
943
975
|
for (const file of allFiles) {
|
|
976
|
+
const normalizedFile = file.path.toLowerCase();
|
|
944
977
|
const basename = path.posix.basename(file.path).toLowerCase();
|
|
945
978
|
const matched = explicitPaths.some((candidate) => {
|
|
946
979
|
const normalized = candidate.toLowerCase();
|
|
947
|
-
return
|
|
980
|
+
return normalizedFile === normalized || (!normalized.includes("/") && basename === normalized);
|
|
948
981
|
});
|
|
949
982
|
if (matched) explicitRows.push({ path: file.path, exact: true });
|
|
950
983
|
}
|
|
951
984
|
addRankedList(scores, explicitRows, "explicit", 3);
|
|
952
|
-
|
|
953
985
|
let lexicalRows = [];
|
|
954
986
|
if (terms.length > 0) {
|
|
955
987
|
lexicalRows = db.prepare(`
|
|
@@ -958,7 +990,6 @@ export async function searchContextIndexV2(cwd, query, options = {}) {
|
|
|
958
990
|
`).all(ftsQuery(terms), Math.max(limit * 4, 30)).filter((row) => pathAllowed(row.path));
|
|
959
991
|
addRankedList(scores, lexicalRows, "lexical", 1.5);
|
|
960
992
|
}
|
|
961
|
-
|
|
962
993
|
const symbolRows = [];
|
|
963
994
|
const symbolStatement = db.prepare(`
|
|
964
995
|
SELECT file_path, name, kind, line, end_line, signature
|
|
@@ -973,13 +1004,11 @@ export async function searchContextIndexV2(cwd, query, options = {}) {
|
|
|
973
1004
|
|
|
974
1005
|
const changedRows = gitChangedFiles(cwd).map((filePath) => ({ path: filePath })).filter((row) => scores.has(row.path));
|
|
975
1006
|
addRankedList(scores, changedRows, "git-change", 0.8);
|
|
976
|
-
|
|
977
1007
|
const testRows = allFiles
|
|
978
1008
|
.filter((file) => /(^|\/)(?:test|tests|__tests__)(\/|$)|(?:\.test|\.spec|_test)\./i.test(file.path))
|
|
979
1009
|
.filter((file) => terms.some((term) => file.path.toLowerCase().includes(term)))
|
|
980
1010
|
.map((file) => ({ path: file.path }));
|
|
981
1011
|
addRankedList(scores, testRows, "test", 1.1);
|
|
982
|
-
|
|
983
1012
|
// Feedback is deliberately positive-only and weak. A file is boosted only
|
|
984
1013
|
// after a prior context pack selected it and the same session actually used
|
|
985
1014
|
// it. New files and unused historical candidates are never penalized.
|
|
@@ -987,12 +1016,13 @@ export async function searchContextIndexV2(cwd, query, options = {}) {
|
|
|
987
1016
|
addRankedList(scores, feedbackRows, "feedback", 0.45);
|
|
988
1017
|
|
|
989
1018
|
const graphSeeds = new Map([...scores.entries()].map(([filePath, value]) => [filePath, value.score]));
|
|
990
|
-
const importRows = db.prepare("SELECT file_path, target_path FROM imports WHERE target_path IS NOT NULL")
|
|
1019
|
+
const importRows = db.prepare("SELECT file_path, specifier, target_path FROM imports WHERE target_path IS NOT NULL")
|
|
991
1020
|
.all()
|
|
992
1021
|
.filter((row) => pathAllowed(row.file_path) && pathAllowed(row.target_path));
|
|
1022
|
+
const directLinks = directExplicitImportLinks(importRows, explicitRows);
|
|
1023
|
+
addRankedList(scores, directLinks.rankingRows, "import", 1.8);
|
|
993
1024
|
const graphRows = personalizedPageRank(importRows, graphSeeds).slice(0, Math.max(limit * 3, 30));
|
|
994
1025
|
addRankedList(scores, graphRows, "graph", 1);
|
|
995
|
-
|
|
996
1026
|
const ranked = [...scores.values()]
|
|
997
1027
|
.filter((candidate) => (
|
|
998
1028
|
shouldIndexPath(candidate.path, { excludePatterns })
|
|
@@ -1012,6 +1042,7 @@ export async function searchContextIndexV2(cwd, query, options = {}) {
|
|
|
1012
1042
|
path: candidate.path,
|
|
1013
1043
|
score: Number(candidate.score.toFixed(6)),
|
|
1014
1044
|
sources: [...candidate.sources],
|
|
1045
|
+
links: directLinks.byPath.get(candidate.path) ?? [],
|
|
1015
1046
|
symbols
|
|
1016
1047
|
};
|
|
1017
1048
|
});
|
|
@@ -1105,31 +1136,79 @@ export async function buildContextPack(cwd, query, options = {}) {
|
|
|
1105
1136
|
}
|
|
1106
1137
|
|
|
1107
1138
|
for (const result of rankedResults) {
|
|
1108
|
-
let text;
|
|
1139
|
+
let text, fileContentHash, sanitizedContentDigest, sensitiveContentRedacted = false;
|
|
1140
|
+
let safeResult = result;
|
|
1109
1141
|
try {
|
|
1110
1142
|
const file = projectFileInfo(cwd, result.path, projectRoot, directoryCache);
|
|
1111
1143
|
if (!file) continue;
|
|
1112
|
-
|
|
1144
|
+
const bytes = fs.readFileSync(file.absolute);
|
|
1145
|
+
const sanitized = redactSensitiveProjectFileText(result.path, bytes.toString("utf8"));
|
|
1146
|
+
if (!sanitized.lineCountPreserved) continue;
|
|
1147
|
+
text = sanitized.text;
|
|
1148
|
+
sensitiveContentRedacted = sanitized.redacted;
|
|
1149
|
+
safeResult = {
|
|
1150
|
+
...result,
|
|
1151
|
+
symbols: result.symbols.map((symbol) => ({
|
|
1152
|
+
...symbol,
|
|
1153
|
+
signature: redactSensitiveProjectFileText(result.path, String(symbol.signature ?? "")).text
|
|
1154
|
+
}))
|
|
1155
|
+
};
|
|
1156
|
+
sanitizedContentDigest = sanitized.redacted
|
|
1157
|
+
? `context-sanitized-file-v1:${sha256(sanitized.text)}`
|
|
1158
|
+
: undefined;
|
|
1159
|
+
fileContentHash = sanitizedContentDigest ?? `context-file-v1:${sha256(bytes)}`;
|
|
1113
1160
|
} catch {
|
|
1114
1161
|
continue;
|
|
1115
1162
|
}
|
|
1116
|
-
const snippets = lineRangesForFile(text,
|
|
1163
|
+
const snippets = lineRangesForFile(text, safeResult, search.terms, includeCode);
|
|
1117
1164
|
const body = snippets.length > 0
|
|
1118
1165
|
? snippets.map((snippet) => `lines ${snippet.start}-${snippet.end}\n${snippet.text}`).join("\n\n")
|
|
1119
|
-
:
|
|
1166
|
+
: safeResult.symbols.slice(0, 8).map((symbol) => `line ${symbol.line}: ${symbol.signature}`).join("\n");
|
|
1120
1167
|
const itemText = [`### ${result.path}`, `why: ${result.sources.join(", ")}`, body].filter(Boolean).join("\n");
|
|
1121
1168
|
const tokens = estimateContextTokens(itemText);
|
|
1122
|
-
const
|
|
1169
|
+
const itemRanges = snippets.length > 0
|
|
1170
|
+
? snippets.map(({ start, end }) => ({ start, end }))
|
|
1171
|
+
: safeResult.symbols.slice(0, 8).map((symbol) => ({ start: symbol.line, end: symbol.endLine ?? symbol.line }));
|
|
1172
|
+
const representation = snippets.length > 0 ? "snippet" : body ? "symbol-map" : "navigation";
|
|
1173
|
+
const item = {
|
|
1174
|
+
...safeResult,
|
|
1175
|
+
text: itemText,
|
|
1176
|
+
estimatedTokens: tokens,
|
|
1177
|
+
truncated: false,
|
|
1178
|
+
fileContentHash,
|
|
1179
|
+
...(sanitizedContentDigest ? { sanitizedContentDigest } : {}),
|
|
1180
|
+
payloadHash: `context-payload-v1:${sha256(itemText)}`,
|
|
1181
|
+
representation,
|
|
1182
|
+
ranges: itemRanges,
|
|
1183
|
+
generation: 1,
|
|
1184
|
+
sensitiveContentRedacted
|
|
1185
|
+
};
|
|
1123
1186
|
if (estimateContextTokens(renderPack([...selected, item])) > budgetTokens) {
|
|
1124
1187
|
if (selected.length === 0) {
|
|
1125
1188
|
const remaining = budgetTokens - estimateContextTokens(renderPack([]));
|
|
1126
1189
|
let chars = Math.max(0, remaining * 4 - 16);
|
|
1127
|
-
let truncated = {
|
|
1190
|
+
let truncated = {
|
|
1191
|
+
...safeResult,
|
|
1192
|
+
text: itemText.slice(0, chars),
|
|
1193
|
+
estimatedTokens: remaining,
|
|
1194
|
+
truncated: true,
|
|
1195
|
+
fileContentHash,
|
|
1196
|
+
...(sanitizedContentDigest ? { sanitizedContentDigest } : {}),
|
|
1197
|
+
payloadHash: "",
|
|
1198
|
+
representation: `${representation}-truncated`,
|
|
1199
|
+
ranges: [],
|
|
1200
|
+
generation: 1,
|
|
1201
|
+
sensitiveContentRedacted
|
|
1202
|
+
};
|
|
1128
1203
|
while (chars > 0 && estimateContextTokens(renderPack([truncated])) > budgetTokens) {
|
|
1129
1204
|
chars = Math.max(0, chars - 16);
|
|
1130
1205
|
truncated = { ...truncated, text: itemText.slice(0, chars) };
|
|
1131
1206
|
}
|
|
1132
|
-
if (chars > 0) selected.push(
|
|
1207
|
+
if (chars > 0) selected.push({
|
|
1208
|
+
...truncated,
|
|
1209
|
+
estimatedTokens: estimateContextTokens(truncated.text),
|
|
1210
|
+
payloadHash: `context-payload-v1:${sha256(truncated.text)}`
|
|
1211
|
+
});
|
|
1133
1212
|
}
|
|
1134
1213
|
continue;
|
|
1135
1214
|
}
|
|
@@ -1203,10 +1282,10 @@ export async function buildTestImpact(cwd, changedFiles = [], options = {}) {
|
|
|
1203
1282
|
export function appendContextTelemetry(cwd, event) {
|
|
1204
1283
|
const paths = contextEnginePaths(cwd);
|
|
1205
1284
|
const record = {
|
|
1206
|
-
schemaVersion: TELEMETRY_SCHEMA_VERSION,
|
|
1207
1285
|
source: "piagent",
|
|
1208
|
-
|
|
1209
|
-
|
|
1286
|
+
...event,
|
|
1287
|
+
schemaVersion: TELEMETRY_SCHEMA_VERSION, telemetrySource: "piagent",
|
|
1288
|
+
recordedAt: typeof event?.recordedAt === "string" ? event.recordedAt : nowIso()
|
|
1210
1289
|
};
|
|
1211
1290
|
appendJsonlBounded(paths.telemetry, record, { maxBytes: MAX_TELEMETRY_FILE_BYTES, mode: 0o600, projectRoot: cwd });
|
|
1212
1291
|
return record;
|
|
@@ -1219,113 +1298,27 @@ export function readContextTelemetry(cwd, options = {}) {
|
|
|
1219
1298
|
return readJsonlTail(target, { limit, maxBytes, projectRoot: cwd });
|
|
1220
1299
|
}
|
|
1221
1300
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1301
|
+
/** Fail-closed telemetry inspection for benchmark evidence. */
|
|
1302
|
+
export function inspectContextTelemetry(cwd, options = {}) {
|
|
1303
|
+
return inspectContextTelemetryIntegrity(cwd, contextEnginePaths(cwd).telemetry, options);
|
|
1224
1304
|
}
|
|
1225
|
-
|
|
1226
1305
|
export function buildContextEfficiencyReport(cwd, options = {}) {
|
|
1227
1306
|
const events = readContextTelemetry(cwd, options);
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
const key = `${event.toolName}:${event.targetHash ?? event.inputHash ?? ""}`;
|
|
1240
|
-
if (seenReads.has(key)) duplicateReads += 1;
|
|
1241
|
-
else seenReads.add(key);
|
|
1242
|
-
}
|
|
1243
|
-
const outputChars = toolResults.reduce((sum, event) => sum + Number(event.outputChars ?? 0), 0);
|
|
1244
|
-
const duplicateOutputChars = toolResults.reduce((sum, event) => sum + (event.repeated ? Number(event.outputChars ?? 0) : 0), 0);
|
|
1245
|
-
const averageActiveTools = prompts.length > 0
|
|
1246
|
-
? prompts.reduce((sum, event) => sum + Number(event.activeTools ?? 0), 0) / prompts.length
|
|
1247
|
-
: 0;
|
|
1248
|
-
const averageSystemPromptTokens = prompts.length > 0
|
|
1249
|
-
? prompts.reduce((sum, event) => sum + Number(event.systemPromptTokens ?? 0), 0) / prompts.length
|
|
1250
|
-
: 0;
|
|
1251
|
-
const averageToolSchemaTokens = prompts.length > 0
|
|
1252
|
-
? prompts.reduce((sum, event) => sum + Number(event.toolSchemaTokens ?? 0), 0) / prompts.length
|
|
1253
|
-
: 0;
|
|
1254
|
-
const lowConfidencePacks = packs.filter((event) => ["none", "low"].includes(event.confidence)).length;
|
|
1255
|
-
const feedback = retrievalFeedback(cwd);
|
|
1256
|
-
const duplicateReadRate = ratio(duplicateReads, readCalls);
|
|
1257
|
-
const duplicateOutputRate = ratio(duplicateOutputChars, outputChars);
|
|
1258
|
-
const schemaShare = ratio(averageToolSchemaTokens, averageSystemPromptTokens);
|
|
1259
|
-
const lowConfidenceRate = ratio(lowConfidencePacks, packs.length);
|
|
1260
|
-
const activeToolPenalty = Math.max(0, Math.min(1, (averageActiveTools - 12) / 24));
|
|
1261
|
-
const wasteScore = Math.round(100 * (
|
|
1262
|
-
duplicateReadRate * 0.3
|
|
1263
|
-
+ duplicateOutputRate * 0.25
|
|
1264
|
-
+ Math.min(1, schemaShare * 3) * 0.2
|
|
1265
|
-
+ lowConfidenceRate * 0.15
|
|
1266
|
-
+ activeToolPenalty * 0.1
|
|
1267
|
-
));
|
|
1268
|
-
const recommendations = [];
|
|
1269
|
-
if (duplicateReadRate > 0.2) recommendations.push("Repeated reads are high; reuse the current working set before searching again.");
|
|
1270
|
-
if (duplicateOutputRate > 0.15) recommendations.push("Repeated tool output is high; prefer delta results and narrower verification.");
|
|
1271
|
-
if (schemaShare > 0.15 || averageActiveTools > 20) recommendations.push("Tool surface is large; activate only the workflow groups needed for the next turn.");
|
|
1272
|
-
if (lowConfidenceRate > 0.4) recommendations.push("Retrieval confidence is low; rebuild the index or run one bounded finder pass.");
|
|
1273
|
-
if (feedback.selected >= 4 && feedback.utilizationRate < 0.45) recommendations.push("Context-pack utilization is low; reduce pack breadth or improve task-specific ranking signals.");
|
|
1274
|
-
if (recommendations.length === 0) recommendations.push("No dominant context waste signal was detected in the sampled events.");
|
|
1275
|
-
const report = {
|
|
1276
|
-
schemaVersion: 1,
|
|
1277
|
-
source: "piagent",
|
|
1278
|
-
generatedAt: nowIso(),
|
|
1279
|
-
sample: {
|
|
1280
|
-
events: events.length,
|
|
1281
|
-
prompts: prompts.length,
|
|
1282
|
-
toolCalls: toolCalls.length,
|
|
1283
|
-
toolResults: toolResults.length,
|
|
1284
|
-
contextPacks: packs.length,
|
|
1285
|
-
compactions: compactions.length
|
|
1286
|
-
},
|
|
1287
|
-
metrics: {
|
|
1288
|
-
averageActiveTools: Number(averageActiveTools.toFixed(2)),
|
|
1289
|
-
averageSystemPromptTokens: Math.round(averageSystemPromptTokens),
|
|
1290
|
-
averageToolSchemaTokens: Math.round(averageToolSchemaTokens),
|
|
1291
|
-
toolSchemaShare: Number(schemaShare.toFixed(4)),
|
|
1292
|
-
readCalls,
|
|
1293
|
-
duplicateReads,
|
|
1294
|
-
duplicateReadRate: Number(duplicateReadRate.toFixed(4)),
|
|
1295
|
-
outputChars,
|
|
1296
|
-
duplicateOutputChars,
|
|
1297
|
-
duplicateOutputRate: Number(duplicateOutputRate.toFixed(4)),
|
|
1298
|
-
lowConfidencePacks,
|
|
1299
|
-
lowConfidenceRate: Number(lowConfidenceRate.toFixed(4)),
|
|
1300
|
-
contextSelections: feedback.selected,
|
|
1301
|
-
contextSelectionsUsed: feedback.used,
|
|
1302
|
-
contextSelectionsUnused: feedback.unused,
|
|
1303
|
-
contextUtilizationRate: Number(feedback.utilizationRate.toFixed(4)),
|
|
1304
|
-
contextWasteScore: wasteScore
|
|
1305
|
-
},
|
|
1306
|
-
methodology: {
|
|
1307
|
-
scoreRange: "0-100; lower is better",
|
|
1308
|
-
weights: {
|
|
1309
|
-
duplicateReads: 0.3,
|
|
1310
|
-
duplicateOutput: 0.25,
|
|
1311
|
-
toolSchemaShare: 0.2,
|
|
1312
|
-
lowConfidenceRetrieval: 0.15,
|
|
1313
|
-
activeToolExcess: 0.1
|
|
1314
|
-
},
|
|
1315
|
-
retrievalFeedback: "Positive-only reranking: a path receives a weak boost only when a prior pack selected it and the same session later used it. Unseen and unused paths are not penalized.",
|
|
1316
|
-
note: "This is an operational signal, not a quality verdict. Compare it with task acceptance and verification results."
|
|
1317
|
-
},
|
|
1318
|
-
recommendations
|
|
1319
|
-
};
|
|
1320
|
-
const paths = contextEnginePaths(cwd);
|
|
1321
|
-
ensurePrivateStateDirectory(cwd, paths.root, "Context report directory");
|
|
1322
|
-
const reportPath = resolveLocalStatePath(cwd, paths.report, { label: "Context efficiency report" });
|
|
1323
|
-
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
1324
|
-
return report;
|
|
1307
|
+
return writeContextEfficiencyReport(cwd, events, {
|
|
1308
|
+
contextEnginePaths: () => contextEnginePaths(cwd),
|
|
1309
|
+
nowIso,
|
|
1310
|
+
retrievalFeedback: () => retrievalFeedback(cwd)
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
function canonicalJson(value) {
|
|
1314
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
1315
|
+
if (!nested || typeof nested !== "object" || Array.isArray(nested)) return nested;
|
|
1316
|
+
return Object.fromEntries(Object.keys(nested).sort().map((key) => [key, nested[key]]));
|
|
1317
|
+
});
|
|
1325
1318
|
}
|
|
1326
1319
|
|
|
1327
1320
|
export function toolResultFingerprint(toolName, input, content) {
|
|
1328
|
-
const inputHash = sha256(
|
|
1321
|
+
const inputHash = sha256(canonicalJson(input ?? {}));
|
|
1329
1322
|
const text = Array.isArray(content)
|
|
1330
1323
|
? content.filter((block) => block && typeof block === "object" && block.type === "text").map((block) => block.text ?? "").join("\n")
|
|
1331
1324
|
: String(content ?? "");
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const PLANNED_CONTEXT_REASON = /^criterion-[a-z0-9_-]+(?:\s|$)/i;
|
|
2
|
+
const RUNTIME_OBSERVED_READ_REASONS = new Set([
|
|
3
|
+
"Runtime observed successful source read.",
|
|
4
|
+
"Runtime observed successful document read."
|
|
5
|
+
]);
|
|
6
|
+
const RUNTIME_CONFIRMED_DELIVERY_REASONS = new Set([
|
|
7
|
+
"Runtime confirmed delivery of a bounded Context Engine navigation pack.",
|
|
8
|
+
"Runtime confirmed delivery of criterion-selected context.",
|
|
9
|
+
"Runtime confirmed delivery of a /context pack message.",
|
|
10
|
+
"Runtime confirmed delivery of an explicit Context Engine tool pack."
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export function isPlannedContextEntry(entry) {
|
|
14
|
+
return Boolean(
|
|
15
|
+
entry
|
|
16
|
+
&& typeof entry.path === "string"
|
|
17
|
+
&& typeof entry.reason === "string"
|
|
18
|
+
&& PLANNED_CONTEXT_REASON.test(entry.reason.trim())
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function isRuntimeOwnedContextEvidenceEntry(entry) {
|
|
23
|
+
if (!entry || typeof entry !== "object") return false;
|
|
24
|
+
const reason = typeof entry.reason === "string" ? entry.reason.trim() : "";
|
|
25
|
+
return RUNTIME_OBSERVED_READ_REASONS.has(reason) || RUNTIME_CONFIRMED_DELIVERY_REASONS.has(reason);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isDurableContextEvidenceEntry(entry) {
|
|
29
|
+
return Boolean(
|
|
30
|
+
entry
|
|
31
|
+
&& typeof entry.path === "string"
|
|
32
|
+
&& entry.path.trim().length > 0
|
|
33
|
+
&& isRuntimeOwnedContextEvidenceEntry(entry)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function durableContextEvidenceEntries(taskOrEntries) {
|
|
38
|
+
const entries = Array.isArray(taskOrEntries)
|
|
39
|
+
? taskOrEntries
|
|
40
|
+
: Array.isArray(taskOrEntries?.contextManifest)
|
|
41
|
+
? taskOrEntries.contextManifest
|
|
42
|
+
: [];
|
|
43
|
+
return entries.filter(isDurableContextEvidenceEntry);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function hasDurableContextEvidence(taskOrEntries) {
|
|
47
|
+
return durableContextEvidenceEntries(taskOrEntries).length > 0;
|
|
48
|
+
}
|