@piagent/platform 1.37.1 → 1.38.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 +47 -0
- package/README.md +5 -3
- package/README.vi.md +2 -2
- package/catalog/capabilities.json +2 -2
- package/docs/command-reference-vietnamese.md +4 -3
- package/docs/distribution-standard.md +6 -6
- package/docs/en/maintainer-guide.md +8 -0
- package/docs/en/workspace-editor.md +222 -0
- package/docs/languages.json +13 -4
- package/docs/operator-manual-vietnamese.md +7 -7
- package/docs/publishing-for-teams.md +2 -0
- package/docs/release-install-policy.md +2 -2
- package/docs/research/ide-m0-spike.md +101 -0
- package/docs/team-onboarding.md +2 -2
- package/docs/vi/maintainer-guide.md +8 -0
- package/docs/vi/workspace-editor.md +217 -0
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/content/en/index.html +4 -4
- package/docs-site/content/en/whats-new.html +17 -0
- package/docs-site/content/index.html +4 -4
- package/docs-site/content/whats-new.html +17 -0
- package/docs-site/context-engine.html +1 -1
- package/docs-site/ecosystem.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/ecosystem.html +1 -1
- package/docs-site/en/index.html +5 -5
- 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 +1 -1
- 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 +18 -1
- package/docs-site/en/windows.html +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +5 -5
- 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 +1 -1
- 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 +18 -1
- package/docs-site/windows.html +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/fixtures/piagent-webui/checkpoint-v1.invalid.json +21 -0
- package/evals/fixtures/piagent-webui/checkpoint-v1.valid.json +20 -0
- package/evals/fixtures/piagent-webui/terminal-v1.invalid.json +17 -0
- package/evals/fixtures/piagent-webui/terminal-v1.valid.json +16 -0
- package/evals/fixtures/piagent-webui/workspace-file-v1.invalid.json +24 -0
- package/evals/fixtures/piagent-webui/workspace-file-v1.valid.json +23 -0
- package/evals/fixtures/piagent-webui/workspace-paths-v1.invalid.json +24 -0
- package/evals/fixtures/piagent-webui/workspace-paths-v1.valid.json +24 -0
- package/evals/fixtures/piagent-webui/workspace-search-v1.invalid.json +26 -0
- package/evals/fixtures/piagent-webui/workspace-search-v1.valid.json +26 -0
- package/evals/fixtures/piagent-webui/workspace-tree-v1.invalid.json +28 -0
- package/evals/fixtures/piagent-webui/workspace-tree-v1.valid.json +27 -0
- package/evals/fixtures/piagent-webui/workspace-write-v1.invalid.json +19 -0
- package/evals/fixtures/piagent-webui/workspace-write-v1.valid.json +19 -0
- package/evals/long-task-soak-v1/README.md +17 -0
- package/evals/long-task-soak-v1/lib/session-loop.mjs +19 -3
- package/evals/long-task-soak-v1/scenarios/ide-editor-terminal.json +19 -0
- package/evals/long-task-soak-v1/surfaces/webui-gateway.mjs +17 -2
- package/evals/long-task-soak-v1/surfaces/workspace-actions.mjs +95 -0
- package/evals/private-holdout-v1/public-exposure.v2.json +2 -2
- package/package.json +7 -3
- package/packages/piagent-core/extensions/fallback-base-policy.ts +1 -1
- package/packages/piagent-core/extensions/guard-types.ts +3 -1
- package/packages/piagent-core/extensions/path-suggestions.ts +137 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +85 -7
- package/packages/piagent-core/extensions/workspace-inspection.ts +145 -0
- package/packages/piagent-core/extensions/workspace-policy.d.ts +7 -0
- package/packages/piagent-core/extensions/workspace-policy.js +3 -0
- package/packages/piagent-core/extensions/workspace-search-index.mjs +8 -0
- package/packages/piagent-core/extensions/workspace-search.ts +74 -0
- package/packages/piagent-core/extensions/workspace-write.ts +116 -0
- package/packages/piagent-core/managed/background-process.mjs +9 -4
- package/packages/piagent-core/managed/boundary-package-proxy.mjs +21 -0
- package/packages/piagent-core/managed/boundary-queue.mjs +24 -0
- package/packages/piagent-core/managed/checkpoint-git.mjs +16 -0
- package/packages/piagent-core/managed/checkpoints.mjs +17 -64
- package/packages/piagent-core/managed/command-tools.mjs +6 -6
- package/packages/piagent-core/managed/continuation.mjs +3 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/backend-api/profile.json +88 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/be-readonly-fe/profile.json +130 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/data/profile.json +88 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/devops/profile.json +90 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/docs/profile.json +74 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/fullstack/profile.json +92 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/generic/profile.json +95 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/mobile/profile.json +86 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/node-typescript/profile.json +74 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/python/profile.json +75 -0
- package/packages/piagent-core/managed/dist/workspace/assets/adapters/web-frontend/profile.json +99 -0
- package/packages/piagent-core/managed/dist/workspace/assets/local-state-path-BrmghxEZ.js +284 -0
- package/packages/piagent-core/managed/dist/workspace/assets/packages/piagent-core/policies/base-policy.json +351 -0
- package/packages/piagent-core/managed/dist/workspace/manifest.json +65 -0
- package/packages/piagent-core/managed/dist/workspace/worker.mjs +7374 -0
- package/packages/piagent-core/managed/dist/workspace/workspace-search-index.mjs +1007 -0
- package/packages/piagent-core/managed/patch-snapshot.mjs +6 -8
- package/packages/piagent-core/managed/permission.mjs +29 -6
- package/packages/piagent-core/managed/repository-operation.mjs +2 -1
- package/packages/piagent-core/managed/review-snapshot-request.mjs +21 -0
- package/packages/piagent-core/managed/sandbox-diagnostic.mjs +2 -0
- package/packages/piagent-core/managed/session-mutation-observations.mjs +37 -0
- package/packages/piagent-core/managed/session.mjs +2 -2
- package/packages/piagent-core/managed/source-revert-requests.mjs +21 -0
- package/packages/piagent-core/managed/terminal-boundary.mjs +88 -0
- package/packages/piagent-core/managed/terminal-channel.mjs +66 -0
- package/packages/piagent-core/managed/terminal-runtime.mjs +64 -0
- package/packages/piagent-core/managed/terminal-seatbelt.mjs +48 -0
- package/packages/piagent-core/managed/terminal-worker.mjs +70 -0
- package/packages/piagent-core/managed/tool-boundary.mjs +45 -35
- package/packages/piagent-core/managed/tool-freshness.mjs +47 -0
- package/packages/piagent-core/managed/tool-worker.mjs +18 -3
- package/packages/piagent-core/managed/workspace-worker-pins.mjs +26 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +2 -1
- package/packages/piagent-core/runtime/checkpoints/checkpoint-authority.ts +43 -0
- package/packages/piagent-core/runtime/checkpoints/checkpoint-refs.mjs +51 -0
- package/packages/piagent-core/runtime/checkpoints/checkpoint-store.mjs +179 -0
- package/packages/piagent-core/runtime/checkpoints/checkpoint-worker.ts +28 -0
- package/packages/piagent-core/runtime/checkpoints/git-adapter.mjs +36 -0
- package/packages/piagent-core/runtime/checkpoints/persistent-history.mjs +96 -0
- package/packages/piagent-core/runtime/checkpoints/personal-git.ts +272 -0
- package/packages/piagent-core/runtime/checkpoints/personal-runtime.ts +158 -0
- package/packages/piagent-core/runtime/checkpoints/prune.mjs +29 -0
- package/packages/piagent-core/runtime/checkpoints/restore-preview.mjs +46 -0
- package/packages/piagent-core/runtime/checkpoints/turn-checkpoints.mjs +167 -0
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +19 -16
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +3 -0
- package/packages/piagent-core/runtime/hooks/tool-call-hook.ts +2 -2
- package/packages/piagent-core/runtime/inspection/diff-projection.ts +2 -1
- package/packages/piagent-core/runtime/inspection/managed-source-revert.ts +90 -0
- package/packages/piagent-core/runtime/inspection/review-state-projection.ts +19 -8
- package/packages/piagent-core/runtime/inspection/session-diff-projection.ts +47 -0
- package/packages/piagent-core/runtime/inspection/session-hunk-provenance.ts +56 -0
- package/packages/piagent-core/runtime/inspection/session-mutation-observer.ts +125 -0
- package/packages/piagent-core/runtime/inspection/source-action-contract.ts +123 -0
- package/packages/piagent-core/runtime/inspection/source-change-projection.ts +3 -9
- package/packages/piagent-core/runtime/inspection/source-mutation-projection.ts +3 -41
- package/packages/piagent-core/runtime/inspection/source-mutation-store.ts +6 -36
- package/packages/piagent-core/runtime/inspection/source-revert-projection.ts +36 -30
- package/packages/piagent-core/runtime/inspection/text-diff.ts +5 -4
- package/packages/piagent-core/runtime/inspection/workspace-worker.ts +79 -0
- package/packages/piagent-core/runtime/policy/source-index-transaction.ts +3 -10
- package/packages/piagent-core/runtime/policy/source-mutation-guard-binding.ts +27 -2
- package/packages/piagent-core/runtime/policy/source-mutation-guard.ts +42 -12
- package/packages/piagent-core/runtime/policy/source-worktree-transaction.ts +10 -5
- package/packages/piagent-core/runtime/policy/workspace-write-authority.ts +51 -0
- package/packages/piagent-core/runtime/process-owner.mjs +20 -0
- package/packages/piagent-core/runtime/process-tree-watch.mjs +46 -0
- package/packages/piagent-core/runtime/quality/model-mutation-proof.ts +8 -5
- package/packages/piagent-core/runtime/registration/permission-commands.ts +10 -2
- package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +1 -1
- package/packages/piagent-core/runtime/session/workspace-member-edits.d.mts +7 -0
- package/packages/piagent-core/runtime/session/workspace-member-edits.mjs +41 -0
- package/packages/piagent-core/security/workspace-file-reader.ts +2 -1
- package/packages/piagent-webui/dist/client/assets/Alert-DXF8-XqE.js +1 -0
- package/packages/piagent-webui/dist/client/assets/Button-DZJosXQi.js +1 -0
- package/packages/piagent-webui/dist/client/assets/DialogTitle-CL-fzCnT.js +3 -0
- package/packages/piagent-webui/dist/client/assets/MarkdownMessage-k4tq1y8s.js +29 -0
- package/packages/piagent-webui/dist/client/assets/TerminalPanel-DvOPgrGI.js +2 -0
- package/packages/piagent-webui/dist/client/assets/TerminalView-B_a16moC.js +9 -0
- package/packages/piagent-webui/dist/client/assets/TerminalView-kHJ-D0s7.css +1 -0
- package/packages/piagent-webui/dist/client/assets/Typography-DMZL1kLS.js +1 -0
- package/packages/piagent-webui/dist/client/assets/WorkspaceCodeEditor-BCiYM9NA.js +2 -0
- package/packages/piagent-webui/dist/client/assets/WorkspaceExplorer-DttpQtpf.js +5 -0
- package/packages/piagent-webui/dist/client/assets/WorkspaceQuickOpen-fgXwQ46B.js +1 -0
- package/packages/piagent-webui/dist/client/assets/bootstrap-CedkNtRG.js +1 -0
- package/packages/piagent-webui/dist/client/assets/dist-B1M7IjfA.js +10 -0
- package/packages/piagent-webui/dist/client/assets/dist-BUYtGaUP.js +1 -0
- package/packages/piagent-webui/dist/client/assets/dist-C82AtDDT.js +6 -0
- package/packages/piagent-webui/dist/client/assets/dist-D9b8j6pJ.js +1 -0
- package/packages/piagent-webui/dist/client/assets/dist-DLfpWwLs.js +23 -0
- package/packages/piagent-webui/dist/client/assets/dist-DcEu_WYV.js +1 -0
- package/packages/piagent-webui/dist/client/assets/dist-_JsfplOG.js +9 -0
- package/packages/piagent-webui/dist/client/assets/dist-idN8f9Fc.js +1 -0
- package/packages/piagent-webui/dist/client/assets/dist-nvgpa2Us.js +1 -0
- package/packages/piagent-webui/dist/client/assets/index-BW8-BUMx.js +102 -0
- package/packages/piagent-webui/dist/client/assets/index-Brs_HjIo.css +1 -0
- package/packages/piagent-webui/dist/client/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/packages/piagent-webui/dist/client/assets/tab-close-HE0XxoJf.js +1 -0
- package/packages/piagent-webui/dist/client/assets/ui-preferences-DiddStu0.js +124 -0
- package/packages/piagent-webui/dist/client/assets/useTheme-_ESOj7MI.js +10 -0
- package/packages/piagent-webui/dist/client/assets/workspace-api-O7auv_td.js +1 -0
- package/packages/piagent-webui/dist/client/index.html +10 -2
- package/packages/piagent-webui/extension/same-session-bridge.ts +2 -3
- package/packages/piagent-webui/extension/source-revert-controller.ts +14 -208
- package/packages/piagent-webui/gateway/checkpoint-sessions.ts +99 -0
- package/packages/piagent-webui/gateway/checkpoint-store.ts +6 -0
- package/packages/piagent-webui/gateway/company-relay.ts +5 -0
- package/packages/piagent-webui/gateway/company-terminal-cache.ts +54 -0
- package/packages/piagent-webui/gateway/company-terminal-channel.ts +117 -0
- package/packages/piagent-webui/gateway/company-terminal-relay.ts +75 -0
- package/packages/piagent-webui/gateway/gateway-protocol-service.ts +12 -0
- package/packages/piagent-webui/gateway/gateway-service.ts +48 -4
- package/packages/piagent-webui/gateway/managed-gateway.mjs +30 -3
- package/packages/piagent-webui/gateway/managed-projection.ts +5 -3
- package/packages/piagent-webui/gateway/managed-source-revert-controller.ts +45 -0
- package/packages/piagent-webui/gateway/managed-source-revert-sessions.ts +74 -0
- package/packages/piagent-webui/gateway/managed-terminal-binding.ts +30 -0
- package/packages/piagent-webui/gateway/managed-terminal-processes.ts +74 -0
- package/packages/piagent-webui/gateway/managed-terminal-sessions.ts +9 -0
- package/packages/piagent-webui/gateway/path-suggestions.ts +1 -124
- package/packages/piagent-webui/gateway/personal-terminal-binding.ts +36 -0
- package/packages/piagent-webui/gateway/personal-terminal-diagnostic.mjs +16 -0
- package/packages/piagent-webui/gateway/personal-terminal-processes.ts +119 -0
- package/packages/piagent-webui/gateway/personal-terminal-sessions.ts +22 -0
- package/packages/piagent-webui/gateway/runtime-session-controls.ts +2 -1
- package/packages/piagent-webui/gateway/session-catalog.ts +3 -2
- package/packages/piagent-webui/gateway/session-command-controller.ts +4 -4
- package/packages/piagent-webui/gateway/session-inspection-registry.ts +29 -5
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +8 -8
- package/packages/piagent-webui/gateway/signed-session-journal.ts +77 -0
- package/packages/piagent-webui/gateway/source-revert-sessions.ts +74 -0
- package/packages/piagent-webui/gateway/source-revert-store.ts +47 -0
- package/packages/piagent-webui/gateway/terminal-command-ledger.ts +33 -0
- package/packages/piagent-webui/gateway/terminal-mentions.ts +29 -0
- package/packages/piagent-webui/gateway/terminal-output-buffer.ts +58 -0
- package/packages/piagent-webui/gateway/terminal-router.ts +12 -0
- package/packages/piagent-webui/gateway/terminal-sessions.ts +247 -0
- package/packages/piagent-webui/gateway/workspace-write-sessions.ts +69 -0
- package/packages/piagent-webui/gateway/workspace-write-store.ts +6 -0
- package/packages/piagent-webui/package.json +14 -2
- package/packages/piagent-webui/server/checkpoint-command.ts +30 -0
- package/packages/piagent-webui/server/checkpoint-controller.ts +87 -0
- package/packages/piagent-webui/server/checkpoint-routes.ts +38 -0
- package/packages/piagent-webui/server/control-command-digest.ts +5 -0
- package/packages/piagent-webui/server/core-inspection-provider.ts +16 -22
- package/packages/piagent-webui/server/gateway-websocket.ts +36 -8
- package/packages/piagent-webui/server/loopback-server.ts +9 -0
- package/packages/piagent-webui/server/read-model-provider.ts +3 -0
- package/packages/piagent-webui/server/read-only-router.ts +3 -0
- package/packages/piagent-webui/server/session-auth.ts +4 -1
- package/packages/piagent-webui/server/session-source-revert-routes.ts +54 -0
- package/packages/piagent-webui/server/source-projection-binding.ts +18 -0
- package/packages/piagent-webui/server/source-revert-controller.ts +225 -0
- package/packages/piagent-webui/server/terminal-command.ts +35 -0
- package/packages/piagent-webui/server/terminal-websocket-stream.ts +56 -0
- package/packages/piagent-webui/server/transcript-projection.ts +2 -1
- package/packages/piagent-webui/server/workspace-read-routes.ts +32 -0
- package/packages/piagent-webui/server/workspace-reader.ts +23 -0
- package/packages/piagent-webui/server/workspace-write-controller.ts +118 -0
- package/packages/piagent-webui/server/workspace-write-routes.ts +38 -0
- package/packages/piagent-webui/shared/terminal-message.ts +23 -0
- package/schemas/piagent-webui/catalog-v1.json +63 -0
- package/schemas/piagent-webui/checkpoint-v1.schema.json +466 -0
- package/schemas/piagent-webui/control-command-v1.schema.json +68 -3
- package/schemas/piagent-webui/diff-v1.schema.json +4 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +123 -3
- package/schemas/piagent-webui/session-catalog-v1.schema.json +1 -1
- package/schemas/piagent-webui/session-command-v1.schema.json +1 -1
- package/schemas/piagent-webui/snapshot-v1.schema.json +2 -1
- package/schemas/piagent-webui/source-revert-v1.schema.json +22 -3
- package/schemas/piagent-webui/terminal-v1.schema.json +495 -0
- package/schemas/piagent-webui/workspace-file-v1.schema.json +79 -0
- package/schemas/piagent-webui/workspace-paths-v1.schema.json +71 -0
- package/schemas/piagent-webui/workspace-search-v1.schema.json +80 -0
- package/schemas/piagent-webui/workspace-tree-v1.schema.json +87 -0
- package/schemas/piagent-webui/workspace-write-v1.schema.json +276 -0
- package/schemas/project-profile.schema.json +8 -3
- package/scripts/company-terminal.mjs +16 -11
- package/scripts/pi-auto.sh +5 -1
- package/scripts/team-doctor.sh +8 -0
- package/packages/piagent-webui/dist/client/assets/MarkdownMessage-CfgyQQbW.js +0 -29
- package/packages/piagent-webui/dist/client/assets/index-HyzWKvDu.js +0 -234
- package/packages/piagent-webui/dist/client/assets/index-_0nGP2n3.css +0 -1
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
import { i as contextIndexExcludeDigest, n as resolveLocalStatePath, r as matchesProtectedPath, s as normalizeContextIndexExcludePatterns, t as ensurePrivateStateDirectory } from "./assets/local-state-path-BrmghxEZ.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
7
|
+
//#region ../piagent-core/extensions/workflow-catalog.ts
|
|
8
|
+
var WORKFLOW_IDS = [
|
|
9
|
+
"task",
|
|
10
|
+
"scout",
|
|
11
|
+
"be-to-fe",
|
|
12
|
+
"discuss",
|
|
13
|
+
"plan",
|
|
14
|
+
"review",
|
|
15
|
+
"commit",
|
|
16
|
+
"pr",
|
|
17
|
+
"onboard",
|
|
18
|
+
"platform-improve"
|
|
19
|
+
];
|
|
20
|
+
var WORKFLOW_ALIASES = Object.freeze(Object.fromEntries([
|
|
21
|
+
{
|
|
22
|
+
id: "task",
|
|
23
|
+
aliases: ["task", "implement"],
|
|
24
|
+
label: "Task",
|
|
25
|
+
description: "Implement a bounded task",
|
|
26
|
+
argumentHint: "<request>",
|
|
27
|
+
requestRequired: true,
|
|
28
|
+
contextWorkflow: "task",
|
|
29
|
+
changeMode: "source-change",
|
|
30
|
+
modelUse: "required",
|
|
31
|
+
recommendedFreshSession: true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "scout",
|
|
35
|
+
aliases: ["scout", "audit"],
|
|
36
|
+
label: "Scout",
|
|
37
|
+
description: "Read-only audit or research",
|
|
38
|
+
argumentHint: "<area/spec/risk>",
|
|
39
|
+
requestRequired: true,
|
|
40
|
+
contextWorkflow: "scout",
|
|
41
|
+
changeMode: "read-only",
|
|
42
|
+
modelUse: "required",
|
|
43
|
+
recommendedFreshSession: true
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "be-to-fe",
|
|
47
|
+
aliases: ["be-to-fe", "befe"],
|
|
48
|
+
label: "BE → FE",
|
|
49
|
+
description: "Implement frontend from a backend contract",
|
|
50
|
+
argumentHint: "<BE spec/change + FE outcome>",
|
|
51
|
+
requestRequired: true,
|
|
52
|
+
contextWorkflow: "task",
|
|
53
|
+
changeMode: "source-change",
|
|
54
|
+
modelUse: "required",
|
|
55
|
+
recommendedFreshSession: true
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "discuss",
|
|
59
|
+
aliases: ["discuss", "clarify"],
|
|
60
|
+
label: "Discuss",
|
|
61
|
+
description: "Clarify before planning or editing",
|
|
62
|
+
argumentHint: "<rough idea>",
|
|
63
|
+
requestRequired: true,
|
|
64
|
+
contextWorkflow: "discuss",
|
|
65
|
+
changeMode: "clarification",
|
|
66
|
+
modelUse: "required",
|
|
67
|
+
recommendedFreshSession: false
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "plan",
|
|
71
|
+
aliases: ["plan"],
|
|
72
|
+
label: "Plan",
|
|
73
|
+
description: "Create an implementation plan",
|
|
74
|
+
argumentHint: "<goal>",
|
|
75
|
+
requestRequired: true,
|
|
76
|
+
contextWorkflow: "plan",
|
|
77
|
+
changeMode: "plan-only",
|
|
78
|
+
modelUse: "required",
|
|
79
|
+
recommendedFreshSession: false
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: "review",
|
|
83
|
+
aliases: ["review"],
|
|
84
|
+
label: "Review",
|
|
85
|
+
description: "Review a diff or source read-only",
|
|
86
|
+
argumentHint: "<target or diff>",
|
|
87
|
+
requestRequired: true,
|
|
88
|
+
contextWorkflow: "review",
|
|
89
|
+
changeMode: "read-only",
|
|
90
|
+
modelUse: "required",
|
|
91
|
+
recommendedFreshSession: false
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "commit",
|
|
95
|
+
aliases: ["commit"],
|
|
96
|
+
label: "Commit",
|
|
97
|
+
description: "Run the guarded local commit workflow",
|
|
98
|
+
argumentHint: "[message]",
|
|
99
|
+
requestRequired: false,
|
|
100
|
+
contextWorkflow: "release",
|
|
101
|
+
changeMode: "git",
|
|
102
|
+
modelUse: "required",
|
|
103
|
+
recommendedFreshSession: false
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "pr",
|
|
107
|
+
aliases: ["pr"],
|
|
108
|
+
label: "PR",
|
|
109
|
+
description: "Prepare a guarded pull request",
|
|
110
|
+
argumentHint: "[title]",
|
|
111
|
+
requestRequired: false,
|
|
112
|
+
contextWorkflow: "release",
|
|
113
|
+
changeMode: "git",
|
|
114
|
+
modelUse: "required",
|
|
115
|
+
recommendedFreshSession: false
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "onboard",
|
|
119
|
+
aliases: ["onboard", "onboard-project"],
|
|
120
|
+
label: "Onboard",
|
|
121
|
+
description: "Run first-read project onboarding",
|
|
122
|
+
argumentHint: "[focus]",
|
|
123
|
+
requestRequired: false,
|
|
124
|
+
contextWorkflow: "onboard",
|
|
125
|
+
changeMode: "onboarding",
|
|
126
|
+
modelUse: "required",
|
|
127
|
+
recommendedFreshSession: true
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "platform-improve",
|
|
131
|
+
aliases: ["platform-improve", "platform"],
|
|
132
|
+
label: "Platform improve",
|
|
133
|
+
description: "Improve Pi Agent Platform itself",
|
|
134
|
+
argumentHint: "<goal + affected area>",
|
|
135
|
+
requestRequired: true,
|
|
136
|
+
contextWorkflow: "task",
|
|
137
|
+
changeMode: "platform",
|
|
138
|
+
modelUse: "required",
|
|
139
|
+
recommendedFreshSession: true
|
|
140
|
+
}
|
|
141
|
+
].flatMap((option) => option.aliases.map((alias) => [alias, option.id]))));
|
|
142
|
+
function workflowCommandNames(options = {}) {
|
|
143
|
+
return options.aliases === false ? [...WORKFLOW_IDS] : Object.keys(WORKFLOW_ALIASES);
|
|
144
|
+
}
|
|
145
|
+
function escapedPattern(value) {
|
|
146
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
147
|
+
}
|
|
148
|
+
/** A regex fragment derived from the catalog, for command/projection boundaries. */
|
|
149
|
+
function workflowCommandPattern(options = {}) {
|
|
150
|
+
return workflowCommandNames(options).sort((left, right) => right.length - left.length).map(escapedPattern).join("|");
|
|
151
|
+
}
|
|
152
|
+
new RegExp(`^/(?:piagent-workflow|workflow)\\s+(${workflowCommandPattern()})(?=\\s|$)\\s*`, "i");
|
|
153
|
+
new RegExp(`^/(${workflowCommandPattern({ aliases: false })})(?=\\s|$)\\s*`, "i");
|
|
154
|
+
new Int32Array(new SharedArrayBuffer(4));
|
|
155
|
+
function readTailText(filePath, maxBytes) {
|
|
156
|
+
try {
|
|
157
|
+
const descriptor = fs.openSync(filePath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
158
|
+
try {
|
|
159
|
+
const stat = fs.fstatSync(descriptor);
|
|
160
|
+
const start = Math.max(0, stat.size - maxBytes);
|
|
161
|
+
const length = stat.size - start;
|
|
162
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
163
|
+
fs.readSync(descriptor, buffer, 0, length, start);
|
|
164
|
+
let text = buffer.toString("utf8");
|
|
165
|
+
if (start > 0) text = text.slice(Math.max(0, text.indexOf("\n") + 1));
|
|
166
|
+
return text;
|
|
167
|
+
} finally {
|
|
168
|
+
fs.closeSync(descriptor);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (error?.code === "ENOENT") return "";
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function readJsonlTail(filePath, options = {}) {
|
|
176
|
+
const limit = Math.max(1, Number.isInteger(options.limit) ? options.limit : 100);
|
|
177
|
+
const maxBytes = Math.max(65536, Number.isInteger(options.maxBytes) ? options.maxBytes : 8388608);
|
|
178
|
+
const safeFilePath = options.projectRoot ? resolveLocalStatePath(options.projectRoot, filePath, { label: "Local JSONL state" }) : filePath;
|
|
179
|
+
const lines = (options.projectRoot ? [resolveLocalStatePath(options.projectRoot, `${filePath}.1`, { label: "Rotated local JSONL state" }), safeFilePath] : [`${safeFilePath}.1`, safeFilePath]).flatMap((source) => readTailText(source, maxBytes).split(/\r?\n/).filter(Boolean));
|
|
180
|
+
const records = [];
|
|
181
|
+
for (const line of lines.slice(-limit)) try {
|
|
182
|
+
records.push(JSON.parse(line));
|
|
183
|
+
} catch {}
|
|
184
|
+
return records;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region ../piagent-core/extensions/context-efficiency-metrics.js
|
|
188
|
+
function contextMetricPartition(event, taskByTurn = /* @__PURE__ */ new Map()) {
|
|
189
|
+
const sessionId = typeof event.sessionId === "string" ? event.sessionId : "";
|
|
190
|
+
const turnId = typeof event.turnId === "string" ? event.turnId : "";
|
|
191
|
+
const taskRunId = typeof event.taskRunId === "string" ? event.taskRunId : taskByTurn.get(`${sessionId}\0${turnId}`) ?? "";
|
|
192
|
+
if (!sessionId || !taskRunId) return "";
|
|
193
|
+
return [
|
|
194
|
+
sessionId,
|
|
195
|
+
taskRunId,
|
|
196
|
+
event.model ?? "unknown",
|
|
197
|
+
event.thinkingLevel ?? "unknown"
|
|
198
|
+
].join("\0");
|
|
199
|
+
}
|
|
200
|
+
var BOUND_IMPORT_STRING = `pi_import_string_(\\d+)`;
|
|
201
|
+
new RegExp(String.raw`(?<![.$\w])(?:import|export)\s+(?:type\s+)?[\w$*{},\s]+?\s+from\s+(${BOUND_IMPORT_STRING})`, "gu"), new RegExp(String.raw`(?<![.$\w])import\s+(${BOUND_IMPORT_STRING})`, "gu"), new RegExp(String.raw`(?<![.$\w])require\s*\(\s*(${BOUND_IMPORT_STRING})\s*\)`, "gu"), new RegExp(String.raw`(?<![.$\w])import\s*\(\s*(${BOUND_IMPORT_STRING})\s*\)`, "gu");
|
|
202
|
+
function directExplicitImportLinks(importRows, explicitRows) {
|
|
203
|
+
const explicitPaths = new Set(explicitRows.map((row) => row.path));
|
|
204
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
205
|
+
const rankingRows = [];
|
|
206
|
+
const add = (candidatePath, kind, explicitPath) => {
|
|
207
|
+
const links = byPath.get(candidatePath) ?? [];
|
|
208
|
+
if (!links.some((link) => link.kind === kind && link.path === explicitPath)) {
|
|
209
|
+
links.push({
|
|
210
|
+
kind,
|
|
211
|
+
path: explicitPath
|
|
212
|
+
});
|
|
213
|
+
rankingRows.push({ path: candidatePath });
|
|
214
|
+
}
|
|
215
|
+
byPath.set(candidatePath, links);
|
|
216
|
+
};
|
|
217
|
+
for (const row of importRows) {
|
|
218
|
+
if (typeof row.specifier !== "string" || !/^\.{1,2}\//.test(row.specifier)) continue;
|
|
219
|
+
if (explicitPaths.has(row.file_path)) add(row.target_path, "explicit-imports-candidate", row.file_path);
|
|
220
|
+
if (explicitPaths.has(row.target_path)) add(row.file_path, "candidate-imports-explicit", row.target_path);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
byPath,
|
|
224
|
+
rankingRows
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region ../piagent-core/extensions/context-query-paths.js
|
|
229
|
+
function normalizeRelative$1(value) {
|
|
230
|
+
return String(value ?? "").replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
231
|
+
}
|
|
232
|
+
function safeProjectRelativeQueryPath(raw) {
|
|
233
|
+
const candidate = normalizeRelative$1(raw);
|
|
234
|
+
if (!candidate || candidate.length > 512 || path.posix.isAbsolute(candidate)) return void 0;
|
|
235
|
+
if (candidate.split("/").some((segment) => !segment || segment === "." || segment === "..")) return void 0;
|
|
236
|
+
return candidate;
|
|
237
|
+
}
|
|
238
|
+
function safeGlobSegment(segment) {
|
|
239
|
+
let bracketDepth = 0;
|
|
240
|
+
let braceDepth = 0;
|
|
241
|
+
for (const character of segment) if (character === "[") {
|
|
242
|
+
if (++bracketDepth > 1) return false;
|
|
243
|
+
} else if (character === "]") {
|
|
244
|
+
if (--bracketDepth < 0) return false;
|
|
245
|
+
} else if (character === "{") {
|
|
246
|
+
if (++braceDepth > 1) return false;
|
|
247
|
+
} else if (character === "}") {
|
|
248
|
+
if (--braceDepth < 0) return false;
|
|
249
|
+
} else if (character === "," && braceDepth === 0 || character === "!" && bracketDepth === 0) return false;
|
|
250
|
+
return bracketDepth === 0 && braceDepth === 0;
|
|
251
|
+
}
|
|
252
|
+
function safeProjectRelativeGlob(raw) {
|
|
253
|
+
const candidate = safeProjectRelativeQueryPath(String(raw ?? "").replace(/[.,]+$/u, ""));
|
|
254
|
+
if (!candidate || !candidate.includes("/") || !/[*?\[\]{}]/u.test(candidate)) return void 0;
|
|
255
|
+
if (!/^[\p{L}\p{N}_.@*?{}\[\]!,-]+(?:\/[\p{L}\p{N}_.@*?{}\[\]!,-]+)+$/u.test(candidate)) return void 0;
|
|
256
|
+
return candidate.split("/").every(safeGlobSegment) ? candidate : void 0;
|
|
257
|
+
}
|
|
258
|
+
function queryPathCandidates(query) {
|
|
259
|
+
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;
|
|
260
|
+
const globPattern = /(?:^|[\s"'(\x60=,:])((?:\.\/)?[\p{L}\p{N}_.@*?{}\[\]!,-]+(?:\/[\p{L}\p{N}_.@*?{}\[\]!,-]+)+)(?=$|[\s"'\x60),:;])/gu;
|
|
261
|
+
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;
|
|
262
|
+
const text = String(query ?? "");
|
|
263
|
+
const candidates = [...text.matchAll(pattern)].flatMap((match) => {
|
|
264
|
+
const raw = match[1], candidate = safeProjectRelativeQueryPath(raw);
|
|
265
|
+
return candidate && (raw.startsWith("./") || explicitRoots.test(candidate) || candidate.split("/").length >= 3 || /(?:^|\/)(?:\.[^/]+|[^/]+\.[\p{L}\p{N}]{1,8})$/u.test(candidate)) ? [{
|
|
266
|
+
candidate,
|
|
267
|
+
index: match.index ?? 0
|
|
268
|
+
}] : [];
|
|
269
|
+
});
|
|
270
|
+
for (const match of text.matchAll(globPattern)) {
|
|
271
|
+
const candidate = safeProjectRelativeGlob(match[1]);
|
|
272
|
+
if (candidate) candidates.push({
|
|
273
|
+
candidate,
|
|
274
|
+
index: match.index ?? 0
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
candidates.sort((left, right) => left.index - right.index);
|
|
278
|
+
return [...new Set(candidates.map((entry) => entry.candidate))];
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
281
|
+
//#region ../piagent-core/extensions/context-engine.js
|
|
282
|
+
var INDEX_SCHEMA_VERSION = 2;
|
|
283
|
+
var RRF_K = 60;
|
|
284
|
+
var MAX_TELEMETRY_EVENTS = 5e4;
|
|
285
|
+
var MAX_TELEMETRY_READ_BYTES = 33554432;
|
|
286
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
287
|
+
".c",
|
|
288
|
+
".cc",
|
|
289
|
+
".cpp",
|
|
290
|
+
".cs",
|
|
291
|
+
".css",
|
|
292
|
+
".dart",
|
|
293
|
+
".ex",
|
|
294
|
+
".exs",
|
|
295
|
+
".go",
|
|
296
|
+
".h",
|
|
297
|
+
".hpp",
|
|
298
|
+
".html",
|
|
299
|
+
".java",
|
|
300
|
+
".js",
|
|
301
|
+
".jsx",
|
|
302
|
+
".kt",
|
|
303
|
+
".kts",
|
|
304
|
+
".lua",
|
|
305
|
+
".m",
|
|
306
|
+
".md",
|
|
307
|
+
".mdx",
|
|
308
|
+
".php",
|
|
309
|
+
".proto",
|
|
310
|
+
".py",
|
|
311
|
+
".rb",
|
|
312
|
+
".rs",
|
|
313
|
+
".scala",
|
|
314
|
+
".scss",
|
|
315
|
+
".sh",
|
|
316
|
+
".sql",
|
|
317
|
+
".svelte",
|
|
318
|
+
".swift",
|
|
319
|
+
".toml",
|
|
320
|
+
".ts",
|
|
321
|
+
".tsx",
|
|
322
|
+
".vue",
|
|
323
|
+
".xml",
|
|
324
|
+
".yaml",
|
|
325
|
+
".yml"
|
|
326
|
+
]);
|
|
327
|
+
var SOURCE_NAMES = /* @__PURE__ */ new Set([
|
|
328
|
+
"AGENTS.md",
|
|
329
|
+
"Dockerfile",
|
|
330
|
+
"Makefile",
|
|
331
|
+
"Procfile",
|
|
332
|
+
"README",
|
|
333
|
+
"README.md",
|
|
334
|
+
"go.mod",
|
|
335
|
+
"package.json",
|
|
336
|
+
"pyproject.toml",
|
|
337
|
+
"requirements.txt",
|
|
338
|
+
"tsconfig.json"
|
|
339
|
+
]);
|
|
340
|
+
var IGNORED_SEGMENTS = /* @__PURE__ */ new Set([
|
|
341
|
+
".git",
|
|
342
|
+
".next",
|
|
343
|
+
".nuxt",
|
|
344
|
+
".output",
|
|
345
|
+
".turbo",
|
|
346
|
+
".venv",
|
|
347
|
+
"build",
|
|
348
|
+
"coverage",
|
|
349
|
+
"dist",
|
|
350
|
+
"node_modules",
|
|
351
|
+
"out",
|
|
352
|
+
"target",
|
|
353
|
+
"vendor"
|
|
354
|
+
]);
|
|
355
|
+
var SECRET_FILE_PATTERNS = [
|
|
356
|
+
/(^|\/)\.env(?:\.|$)/i,
|
|
357
|
+
/(^|\/)auth\.json$/i,
|
|
358
|
+
/(^|\/)(?:credentials?|secrets?|tokens?)\.json$/i,
|
|
359
|
+
/(^|\/)\.pi\/settings\.json$/i,
|
|
360
|
+
/(^|\/)\.pi\/piagent-profile(?:\.lock)?\.json$/i,
|
|
361
|
+
/(^|\/)\.pi\/piagent-state(?:\/|$)/i
|
|
362
|
+
];
|
|
363
|
+
var STOP_TERMS = /* @__PURE__ */ new Set([
|
|
364
|
+
"about",
|
|
365
|
+
"after",
|
|
366
|
+
"agent",
|
|
367
|
+
"before",
|
|
368
|
+
"build",
|
|
369
|
+
"change",
|
|
370
|
+
"check",
|
|
371
|
+
"code",
|
|
372
|
+
"could",
|
|
373
|
+
"file",
|
|
374
|
+
"from",
|
|
375
|
+
"have",
|
|
376
|
+
"into",
|
|
377
|
+
"just",
|
|
378
|
+
"make",
|
|
379
|
+
"need",
|
|
380
|
+
"please",
|
|
381
|
+
"project",
|
|
382
|
+
"should",
|
|
383
|
+
"task",
|
|
384
|
+
"that",
|
|
385
|
+
"them",
|
|
386
|
+
"then",
|
|
387
|
+
"there",
|
|
388
|
+
"these",
|
|
389
|
+
"this",
|
|
390
|
+
"through",
|
|
391
|
+
"update",
|
|
392
|
+
"using",
|
|
393
|
+
"want",
|
|
394
|
+
"what",
|
|
395
|
+
"when",
|
|
396
|
+
"where",
|
|
397
|
+
"which",
|
|
398
|
+
"with",
|
|
399
|
+
"would",
|
|
400
|
+
"anh",
|
|
401
|
+
"cho",
|
|
402
|
+
"cua",
|
|
403
|
+
"em",
|
|
404
|
+
"giup",
|
|
405
|
+
"lam",
|
|
406
|
+
"minh",
|
|
407
|
+
"muon",
|
|
408
|
+
"nay",
|
|
409
|
+
"trong",
|
|
410
|
+
"voi"
|
|
411
|
+
]);
|
|
412
|
+
var sqliteModulePromise;
|
|
413
|
+
var retrievalFeedbackCache = /* @__PURE__ */ new Map();
|
|
414
|
+
function normalizeRelative(value) {
|
|
415
|
+
return String(value ?? "").replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
416
|
+
}
|
|
417
|
+
function clampInteger(value, fallback, min, max) {
|
|
418
|
+
const number = Number(value);
|
|
419
|
+
if (!Number.isFinite(number)) return fallback;
|
|
420
|
+
return Math.max(min, Math.min(max, Math.trunc(number)));
|
|
421
|
+
}
|
|
422
|
+
function ratio(numerator, denominator) {
|
|
423
|
+
return denominator > 0 ? numerator / denominator : 0;
|
|
424
|
+
}
|
|
425
|
+
function requireExplicitExcludePatterns(options, operation) {
|
|
426
|
+
if (!Array.isArray(options?.excludePatterns)) throw new TypeError(`${operation} requires an explicit excludePatterns array`);
|
|
427
|
+
return normalizeContextIndexExcludePatterns(options.excludePatterns);
|
|
428
|
+
}
|
|
429
|
+
function contextEnginePaths(cwd) {
|
|
430
|
+
const root = path.join(cwd, ".pi", "piagent-state", "context-engine");
|
|
431
|
+
return {
|
|
432
|
+
root,
|
|
433
|
+
database: path.join(root, "context-v2.sqlite"),
|
|
434
|
+
telemetry: path.join(root, "events.jsonl"),
|
|
435
|
+
report: path.join(root, "efficiency-report.json")
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function hardenContextDatabaseArtifacts(cwd) {
|
|
439
|
+
const database = resolveLocalStatePath(cwd, contextEnginePaths(cwd).database, { label: "Context index database" });
|
|
440
|
+
for (const artifact of [
|
|
441
|
+
database,
|
|
442
|
+
`${database}-wal`,
|
|
443
|
+
`${database}-shm`
|
|
444
|
+
]) try {
|
|
445
|
+
if (!fs.lstatSync(artifact).isFile()) throw new Error(`Context index storage path must be a regular file: ${artifact}`);
|
|
446
|
+
fs.chmodSync(artifact, 384);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (error?.code !== "ENOENT") throw error;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
async function loadSqlite() {
|
|
452
|
+
if (!sqliteModulePromise) sqliteModulePromise = (async () => {
|
|
453
|
+
const emitWarning = process.emitWarning;
|
|
454
|
+
process.emitWarning = function filteredWarning(warning, ...args) {
|
|
455
|
+
if ((warning instanceof Error ? warning.message : String(warning)).includes("SQLite is an experimental feature")) return;
|
|
456
|
+
return emitWarning.call(process, warning, ...args);
|
|
457
|
+
};
|
|
458
|
+
try {
|
|
459
|
+
return await import("node:sqlite");
|
|
460
|
+
} finally {
|
|
461
|
+
process.emitWarning = emitWarning;
|
|
462
|
+
}
|
|
463
|
+
})();
|
|
464
|
+
return sqliteModulePromise;
|
|
465
|
+
}
|
|
466
|
+
async function openDatabase(cwd, { create = true } = {}) {
|
|
467
|
+
const paths = contextEnginePaths(cwd);
|
|
468
|
+
const database = resolveLocalStatePath(cwd, paths.database, { label: "Context index database" });
|
|
469
|
+
if (!create && !fs.existsSync(database)) return void 0;
|
|
470
|
+
if (create) ensurePrivateStateDirectory(cwd, paths.root, "Context index storage root");
|
|
471
|
+
else {
|
|
472
|
+
const existingRoot = resolveLocalStatePath(cwd, paths.root, {
|
|
473
|
+
label: "Context index storage root",
|
|
474
|
+
kind: "directory"
|
|
475
|
+
});
|
|
476
|
+
try {
|
|
477
|
+
fs.chmodSync(existingRoot, 448);
|
|
478
|
+
} catch {}
|
|
479
|
+
}
|
|
480
|
+
hardenContextDatabaseArtifacts(cwd);
|
|
481
|
+
const descriptor = fs.openSync(database, fs.constants.O_RDWR | (create ? fs.constants.O_CREAT : 0) | (fs.constants.O_NOFOLLOW ?? 0), 384);
|
|
482
|
+
try {
|
|
483
|
+
fs.fchmodSync(descriptor, 384);
|
|
484
|
+
} finally {
|
|
485
|
+
fs.closeSync(descriptor);
|
|
486
|
+
}
|
|
487
|
+
const { DatabaseSync } = await loadSqlite();
|
|
488
|
+
const db = new DatabaseSync(database);
|
|
489
|
+
try {
|
|
490
|
+
db.exec(`
|
|
491
|
+
PRAGMA journal_mode = WAL;
|
|
492
|
+
PRAGMA synchronous = NORMAL;
|
|
493
|
+
PRAGMA foreign_keys = ON;
|
|
494
|
+
PRAGMA secure_delete = ON;
|
|
495
|
+
CREATE TABLE IF NOT EXISTS metadata (
|
|
496
|
+
key TEXT PRIMARY KEY,
|
|
497
|
+
value TEXT NOT NULL
|
|
498
|
+
);
|
|
499
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
500
|
+
path TEXT PRIMARY KEY,
|
|
501
|
+
hash TEXT NOT NULL,
|
|
502
|
+
bytes INTEGER NOT NULL,
|
|
503
|
+
mtime_ms REAL NOT NULL,
|
|
504
|
+
language TEXT NOT NULL,
|
|
505
|
+
indexed_at TEXT NOT NULL
|
|
506
|
+
);
|
|
507
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
508
|
+
id INTEGER PRIMARY KEY,
|
|
509
|
+
file_path TEXT NOT NULL REFERENCES files(path) ON DELETE CASCADE,
|
|
510
|
+
name TEXT NOT NULL,
|
|
511
|
+
kind TEXT NOT NULL,
|
|
512
|
+
line INTEGER NOT NULL,
|
|
513
|
+
end_line INTEGER NOT NULL,
|
|
514
|
+
signature TEXT NOT NULL
|
|
515
|
+
);
|
|
516
|
+
CREATE INDEX IF NOT EXISTS symbols_name_idx ON symbols(name);
|
|
517
|
+
CREATE INDEX IF NOT EXISTS symbols_file_idx ON symbols(file_path);
|
|
518
|
+
CREATE TABLE IF NOT EXISTS imports (
|
|
519
|
+
id INTEGER PRIMARY KEY,
|
|
520
|
+
file_path TEXT NOT NULL REFERENCES files(path) ON DELETE CASCADE,
|
|
521
|
+
specifier TEXT NOT NULL,
|
|
522
|
+
target_path TEXT,
|
|
523
|
+
line INTEGER NOT NULL
|
|
524
|
+
);
|
|
525
|
+
CREATE INDEX IF NOT EXISTS imports_file_idx ON imports(file_path);
|
|
526
|
+
CREATE INDEX IF NOT EXISTS imports_target_idx ON imports(target_path);
|
|
527
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS file_fts USING fts5(
|
|
528
|
+
path UNINDEXED,
|
|
529
|
+
body,
|
|
530
|
+
symbol_names,
|
|
531
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
532
|
+
);
|
|
533
|
+
`);
|
|
534
|
+
const ftsSecureDelete = db.prepare(`
|
|
535
|
+
SELECT v
|
|
536
|
+
FROM file_fts_config
|
|
537
|
+
WHERE k = 'secure-delete'
|
|
538
|
+
`).get();
|
|
539
|
+
if (Number(ftsSecureDelete?.v) !== 1) db.exec("INSERT INTO file_fts(file_fts, rank) VALUES('secure-delete', 1)");
|
|
540
|
+
hardenContextDatabaseArtifacts(cwd);
|
|
541
|
+
return db;
|
|
542
|
+
} catch (error) {
|
|
543
|
+
db.close();
|
|
544
|
+
throw error;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function metadataMap(db) {
|
|
548
|
+
const result = {};
|
|
549
|
+
for (const row of db.prepare("SELECT key, value FROM metadata").all()) result[row.key] = row.value;
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
function shouldIndexPath(relativePath, options = {}) {
|
|
553
|
+
const rel = normalizeRelative(relativePath);
|
|
554
|
+
if (!rel || path.isAbsolute(rel) || rel.startsWith("../")) return false;
|
|
555
|
+
if (rel.split("/").some((segment) => IGNORED_SEGMENTS.has(segment))) return false;
|
|
556
|
+
if (SECRET_FILE_PATTERNS.some((pattern) => pattern.test(rel))) return false;
|
|
557
|
+
if (matchesProtectedPath(rel, options.excludePatterns ?? [])) return false;
|
|
558
|
+
const name = path.posix.basename(rel);
|
|
559
|
+
return SOURCE_NAMES.has(name) || SOURCE_EXTENSIONS.has(path.posix.extname(name).toLowerCase());
|
|
560
|
+
}
|
|
561
|
+
function projectFileInfo(cwd, relativePath, root = fs.realpathSync.native(cwd), directoryCache = /* @__PURE__ */ new Map()) {
|
|
562
|
+
const expected = path.resolve(root, relativePath);
|
|
563
|
+
if (expected !== root && !expected.startsWith(`${root}${path.sep}`)) return void 0;
|
|
564
|
+
try {
|
|
565
|
+
const directPath = path.resolve(cwd, relativePath);
|
|
566
|
+
const directParent = path.dirname(directPath);
|
|
567
|
+
let canonicalParent = directoryCache.get(directParent);
|
|
568
|
+
if (canonicalParent === void 0) {
|
|
569
|
+
canonicalParent = fs.realpathSync.native(directParent);
|
|
570
|
+
directoryCache.set(directParent, canonicalParent);
|
|
571
|
+
}
|
|
572
|
+
if (canonicalParent !== path.dirname(expected)) return void 0;
|
|
573
|
+
const direct = fs.lstatSync(directPath);
|
|
574
|
+
if (direct.isSymbolicLink() || !direct.isFile()) return void 0;
|
|
575
|
+
return {
|
|
576
|
+
absolute: expected,
|
|
577
|
+
stat: direct
|
|
578
|
+
};
|
|
579
|
+
} catch {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function gitFileList(cwd) {
|
|
584
|
+
try {
|
|
585
|
+
return execFileSync("git", [
|
|
586
|
+
"-C",
|
|
587
|
+
cwd,
|
|
588
|
+
"ls-files",
|
|
589
|
+
"--cached",
|
|
590
|
+
"--others",
|
|
591
|
+
"--exclude-standard",
|
|
592
|
+
"-z"
|
|
593
|
+
], {
|
|
594
|
+
encoding: "utf8",
|
|
595
|
+
maxBuffer: 33554432,
|
|
596
|
+
stdio: [
|
|
597
|
+
"ignore",
|
|
598
|
+
"pipe",
|
|
599
|
+
"ignore"
|
|
600
|
+
]
|
|
601
|
+
}).split("\0").filter(Boolean).map(normalizeRelative);
|
|
602
|
+
} catch {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function foldSearchSignal(value) {
|
|
607
|
+
return String(value ?? "").normalize("NFKD").replace(/\p{M}/gu, "").replace(/[đĐ]/g, (character) => character === "Đ" ? "D" : "d").toLowerCase();
|
|
608
|
+
}
|
|
609
|
+
function tokenizeQuery(query) {
|
|
610
|
+
const terms = String(query ?? "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll("_", " ").replaceAll("-", " ").match(/[\p{L}][\p{L}\p{N}]{1,63}/gu) ?? [];
|
|
611
|
+
return [...new Set(terms.map((term) => term.toLowerCase()).filter((term) => !STOP_TERMS.has(foldSearchSignal(term))))].slice(0, 16);
|
|
612
|
+
}
|
|
613
|
+
function ftsQuery(terms) {
|
|
614
|
+
return terms.map((term) => `"${term.replaceAll("\"", "\"\"")}"`).join(" OR ");
|
|
615
|
+
}
|
|
616
|
+
function gitChangedFiles(cwd) {
|
|
617
|
+
try {
|
|
618
|
+
return execFileSync("git", [
|
|
619
|
+
"-C",
|
|
620
|
+
cwd,
|
|
621
|
+
"diff",
|
|
622
|
+
"--name-only",
|
|
623
|
+
"--diff-filter=ACMR",
|
|
624
|
+
"HEAD"
|
|
625
|
+
], {
|
|
626
|
+
encoding: "utf8",
|
|
627
|
+
stdio: [
|
|
628
|
+
"ignore",
|
|
629
|
+
"pipe",
|
|
630
|
+
"ignore"
|
|
631
|
+
]
|
|
632
|
+
}).split(/\r?\n/).map(normalizeRelative).filter(Boolean);
|
|
633
|
+
} catch {
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async function contextIndexV2Status(cwd, options = {}) {
|
|
638
|
+
const expectedExcludePatterns = requireExplicitExcludePatterns(options, "contextIndexV2Status");
|
|
639
|
+
const expectedExcludeDigest = contextIndexExcludeDigest(expectedExcludePatterns);
|
|
640
|
+
const paths = contextEnginePaths(cwd);
|
|
641
|
+
const projectRoot = fs.realpathSync.native(cwd);
|
|
642
|
+
const directoryCache = /* @__PURE__ */ new Map();
|
|
643
|
+
const database = resolveLocalStatePath(cwd, paths.database, { label: "Context index database" });
|
|
644
|
+
if (!fs.existsSync(database)) return {
|
|
645
|
+
schemaVersion: INDEX_SCHEMA_VERSION,
|
|
646
|
+
exists: false,
|
|
647
|
+
path: normalizeRelative(path.relative(cwd, paths.database)),
|
|
648
|
+
stale: false,
|
|
649
|
+
policyStale: false,
|
|
650
|
+
expectedExcludeDigest,
|
|
651
|
+
purgePending: false,
|
|
652
|
+
warnings: ["index-v2 missing"]
|
|
653
|
+
};
|
|
654
|
+
const db = await openDatabase(cwd, { create: false });
|
|
655
|
+
try {
|
|
656
|
+
const metadata = metadataMap(db);
|
|
657
|
+
const fileRows = db.prepare("SELECT path, bytes, mtime_ms FROM files").all();
|
|
658
|
+
const files = fileRows.length;
|
|
659
|
+
const symbols = Number(db.prepare("SELECT COUNT(*) count FROM symbols").get().count);
|
|
660
|
+
const imports = Number(db.prepare("SELECT COUNT(*) count FROM imports").get().count);
|
|
661
|
+
const indexedPaths = new Set(fileRows.map((row) => row.path));
|
|
662
|
+
const storedExcludeDigest = metadata.excludeDigest;
|
|
663
|
+
const purgePending = metadata.purgePending === "1";
|
|
664
|
+
const excludeDigestStale = typeof storedExcludeDigest !== "string" || storedExcludeDigest !== expectedExcludeDigest;
|
|
665
|
+
const policyStale = purgePending || excludeDigestStale;
|
|
666
|
+
const statChanged = [];
|
|
667
|
+
for (const row of fileRows) {
|
|
668
|
+
if (!shouldIndexPath(row.path, { excludePatterns: expectedExcludePatterns })) continue;
|
|
669
|
+
const file = projectFileInfo(cwd, row.path, projectRoot, directoryCache);
|
|
670
|
+
if (!file || file.stat.size !== Number(row.bytes) || Math.abs(file.stat.mtimeMs - Number(row.mtime_ms)) >= .5) statChanged.push(row.path);
|
|
671
|
+
}
|
|
672
|
+
let skippedPaths = /* @__PURE__ */ new Set();
|
|
673
|
+
try {
|
|
674
|
+
const parsed = JSON.parse(metadata.skippedPaths ?? "[]");
|
|
675
|
+
if (Array.isArray(parsed)) skippedPaths = new Set(parsed.filter((value) => typeof value === "string"));
|
|
676
|
+
} catch {
|
|
677
|
+
skippedPaths = /* @__PURE__ */ new Set();
|
|
678
|
+
}
|
|
679
|
+
const listed = gitFileList(cwd);
|
|
680
|
+
const newlyVisible = listed ? listed.filter((file) => !indexedPaths.has(file) && !skippedPaths.has(file) && shouldIndexPath(file, { excludePatterns: expectedExcludePatterns })) : [];
|
|
681
|
+
const stalePaths = [.../* @__PURE__ */ new Set([...statChanged, ...newlyVisible])];
|
|
682
|
+
const warnings = [];
|
|
683
|
+
if (purgePending) warnings.push("context index storage purge pending");
|
|
684
|
+
if (excludeDigestStale) warnings.push("context index exclusion policy changed");
|
|
685
|
+
if (stalePaths.length > 0) warnings.push(`${stalePaths.length} changed file(s) since the last index build`);
|
|
686
|
+
return {
|
|
687
|
+
schemaVersion: Number(metadata.schemaVersion ?? INDEX_SCHEMA_VERSION),
|
|
688
|
+
exists: true,
|
|
689
|
+
path: normalizeRelative(path.relative(cwd, paths.database)),
|
|
690
|
+
builtAt: metadata.builtAt,
|
|
691
|
+
files,
|
|
692
|
+
symbols,
|
|
693
|
+
imports,
|
|
694
|
+
stale: policyStale || stalePaths.length > 0,
|
|
695
|
+
policyStale,
|
|
696
|
+
purgePending,
|
|
697
|
+
excludePolicyVersion: metadata.excludePolicyVersion,
|
|
698
|
+
excludeDigest: storedExcludeDigest,
|
|
699
|
+
expectedExcludeDigest,
|
|
700
|
+
stalePaths: stalePaths.slice(0, 20),
|
|
701
|
+
warnings
|
|
702
|
+
};
|
|
703
|
+
} finally {
|
|
704
|
+
db.close();
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function addRankedList(scores, rows, source, weight = 1) {
|
|
708
|
+
rows.forEach((row, index) => {
|
|
709
|
+
const filePath = row.path ?? row.file_path;
|
|
710
|
+
if (!filePath) return;
|
|
711
|
+
const current = scores.get(filePath) ?? {
|
|
712
|
+
path: filePath,
|
|
713
|
+
score: 0,
|
|
714
|
+
sources: /* @__PURE__ */ new Set(),
|
|
715
|
+
rows: []
|
|
716
|
+
};
|
|
717
|
+
current.score += weight / (RRF_K + index + 1);
|
|
718
|
+
current.sources.add(source);
|
|
719
|
+
current.rows.push({
|
|
720
|
+
source,
|
|
721
|
+
...row
|
|
722
|
+
});
|
|
723
|
+
scores.set(filePath, current);
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
function personalizedPageRank(importRows, seeds, iterations = 8) {
|
|
727
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
728
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
729
|
+
for (const row of importRows) {
|
|
730
|
+
if (!row.target_path) continue;
|
|
731
|
+
nodes.add(row.file_path);
|
|
732
|
+
nodes.add(row.target_path);
|
|
733
|
+
const targets = outgoing.get(row.file_path) ?? /* @__PURE__ */ new Set();
|
|
734
|
+
targets.add(row.target_path);
|
|
735
|
+
outgoing.set(row.file_path, targets);
|
|
736
|
+
}
|
|
737
|
+
for (const seed of seeds.keys()) nodes.add(seed);
|
|
738
|
+
if (nodes.size === 0) return [];
|
|
739
|
+
const seedTotal = [...seeds.values()].reduce((sum, value) => sum + Math.max(value, 0), 0) || 1;
|
|
740
|
+
const teleport = new Map([...nodes].map((node) => [node, (seeds.get(node) ?? 0) / seedTotal]));
|
|
741
|
+
let rank = new Map(teleport);
|
|
742
|
+
const alpha = .85;
|
|
743
|
+
for (let iteration = 0; iteration < iterations; iteration += 1) {
|
|
744
|
+
const next = new Map([...nodes].map((node) => [node, .15000000000000002 * (teleport.get(node) ?? 0)]));
|
|
745
|
+
let dangling = 0;
|
|
746
|
+
for (const node of nodes) {
|
|
747
|
+
const targets = outgoing.get(node);
|
|
748
|
+
if (!targets?.size) {
|
|
749
|
+
dangling += rank.get(node) ?? 0;
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
const share = alpha * (rank.get(node) ?? 0) / targets.size;
|
|
753
|
+
for (const target of targets) next.set(target, (next.get(target) ?? 0) + share);
|
|
754
|
+
}
|
|
755
|
+
for (const node of nodes) next.set(node, (next.get(node) ?? 0) + alpha * dangling * (teleport.get(node) ?? 1 / nodes.size));
|
|
756
|
+
rank = next;
|
|
757
|
+
}
|
|
758
|
+
return [...rank.entries()].sort((left, right) => right[1] - left[1]).map(([filePath, score]) => ({
|
|
759
|
+
path: filePath,
|
|
760
|
+
graphScore: score
|
|
761
|
+
}));
|
|
762
|
+
}
|
|
763
|
+
function retrievalFeedback(cwd) {
|
|
764
|
+
let signature = "missing";
|
|
765
|
+
try {
|
|
766
|
+
const target = resolveLocalStatePath(cwd, contextEnginePaths(cwd).telemetry, { label: "Context telemetry" });
|
|
767
|
+
const stat = fs.statSync(target);
|
|
768
|
+
signature = `${stat.size}:${stat.mtimeMs}`;
|
|
769
|
+
} catch {
|
|
770
|
+
return {
|
|
771
|
+
rows: [],
|
|
772
|
+
observedSelected: 0,
|
|
773
|
+
selected: 0,
|
|
774
|
+
used: 0,
|
|
775
|
+
unused: 0,
|
|
776
|
+
utilizationRate: 0,
|
|
777
|
+
fallbackRereads: 0,
|
|
778
|
+
fallbackRereadRate: 0
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
const cached = retrievalFeedbackCache.get(cwd);
|
|
782
|
+
if (cached?.signature === signature) return cached.value;
|
|
783
|
+
const events = readContextTelemetry(cwd, {
|
|
784
|
+
limit: 12e3,
|
|
785
|
+
maxBytes: 12582912
|
|
786
|
+
});
|
|
787
|
+
const selectedCounts = /* @__PURE__ */ new Map();
|
|
788
|
+
const usedCounts = /* @__PURE__ */ new Map();
|
|
789
|
+
const fallbackReadCounts = /* @__PURE__ */ new Map();
|
|
790
|
+
const latestPackByPartition = /* @__PURE__ */ new Map();
|
|
791
|
+
const taskByTurn = new Map(events.filter((event) => event.event === "turn_task_bound" && typeof event.sessionId === "string" && typeof event.turnId === "string" && typeof event.taskRunId === "string").map((event) => [`${event.sessionId}\0${event.turnId}`, event.taskRunId]));
|
|
792
|
+
let observedSelected = 0, selected = 0, used = 0, fallbackRereads = 0;
|
|
793
|
+
const eventPaths = (event) => [...new Set([...Array.isArray(event.selectedPaths) ? event.selectedPaths : [], ...Array.isArray(event.selectedItems) ? event.selectedItems.map((item) => item?.path) : []].map(normalizeRelative).filter((filePath) => shouldIndexPath(filePath, {})))];
|
|
794
|
+
const successfulMutationPaths = (event) => {
|
|
795
|
+
if (event.event !== "tool_result" || event.isError === true) return [];
|
|
796
|
+
const explicit = [
|
|
797
|
+
event.changedPaths,
|
|
798
|
+
event.mutationPaths,
|
|
799
|
+
event.targetPaths
|
|
800
|
+
].find((value) => Array.isArray(value));
|
|
801
|
+
if (explicit) return [...new Set(explicit.map(normalizeRelative).filter(Boolean))];
|
|
802
|
+
const toolName = String(event.toolName ?? "").toLowerCase();
|
|
803
|
+
if (![
|
|
804
|
+
"write",
|
|
805
|
+
"edit",
|
|
806
|
+
"apply_patch",
|
|
807
|
+
"patch"
|
|
808
|
+
].includes(toolName)) return [];
|
|
809
|
+
const targetPath = normalizeRelative(event.targetPath);
|
|
810
|
+
return targetPath ? [targetPath] : [];
|
|
811
|
+
};
|
|
812
|
+
for (const event of events) {
|
|
813
|
+
const partition = contextMetricPartition(event, taskByTurn);
|
|
814
|
+
if (event.event === "context_pack_injected") {
|
|
815
|
+
const selectedPaths = eventPaths(event);
|
|
816
|
+
observedSelected += selectedPaths.length;
|
|
817
|
+
if (!partition) continue;
|
|
818
|
+
const pack = {
|
|
819
|
+
selectedPaths: new Set(selectedPaths),
|
|
820
|
+
usedPaths: /* @__PURE__ */ new Set(),
|
|
821
|
+
fallbackReadPaths: /* @__PURE__ */ new Set()
|
|
822
|
+
};
|
|
823
|
+
latestPackByPartition.set(partition, pack);
|
|
824
|
+
for (const filePath of selectedPaths) {
|
|
825
|
+
selectedCounts.set(filePath, (selectedCounts.get(filePath) ?? 0) + 1);
|
|
826
|
+
selected += 1;
|
|
827
|
+
}
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (!partition) continue;
|
|
831
|
+
const pack = latestPackByPartition.get(partition);
|
|
832
|
+
if (!pack) continue;
|
|
833
|
+
const targetPath = normalizeRelative(event.targetPath);
|
|
834
|
+
if (event.event === "tool_call" && [
|
|
835
|
+
"read",
|
|
836
|
+
"grep",
|
|
837
|
+
"find",
|
|
838
|
+
"ls"
|
|
839
|
+
].includes(event.toolName) && targetPath && pack.selectedPaths.has(targetPath) && !pack.fallbackReadPaths.has(targetPath)) {
|
|
840
|
+
pack.fallbackReadPaths.add(targetPath);
|
|
841
|
+
fallbackReadCounts.set(targetPath, (fallbackReadCounts.get(targetPath) ?? 0) + 1);
|
|
842
|
+
fallbackRereads += 1;
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
for (const mutationPath of successfulMutationPaths(event)) {
|
|
846
|
+
if (!pack.selectedPaths.has(mutationPath) || pack.usedPaths.has(mutationPath)) continue;
|
|
847
|
+
pack.usedPaths.add(mutationPath);
|
|
848
|
+
usedCounts.set(mutationPath, (usedCounts.get(mutationPath) ?? 0) + 1);
|
|
849
|
+
used += 1;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
const value = {
|
|
853
|
+
rows: [...usedCounts.entries()].map(([filePath, usedCount]) => {
|
|
854
|
+
const selectedCount = selectedCounts.get(filePath) ?? usedCount;
|
|
855
|
+
const utilization = ratio(usedCount, selectedCount);
|
|
856
|
+
const evidence = Math.min(1, selectedCount / 3);
|
|
857
|
+
return {
|
|
858
|
+
path: filePath,
|
|
859
|
+
selectedCount,
|
|
860
|
+
usedCount,
|
|
861
|
+
fallbackRereadCount: fallbackReadCounts.get(filePath) ?? 0,
|
|
862
|
+
feedbackScore: utilization * evidence
|
|
863
|
+
};
|
|
864
|
+
}).sort((left, right) => right.feedbackScore - left.feedbackScore || right.usedCount - left.usedCount || left.path.localeCompare(right.path)),
|
|
865
|
+
observedSelected,
|
|
866
|
+
selected,
|
|
867
|
+
used,
|
|
868
|
+
unused: Math.max(0, selected - used),
|
|
869
|
+
utilizationRate: ratio(used, selected),
|
|
870
|
+
fallbackRereads,
|
|
871
|
+
fallbackRereadRate: ratio(fallbackRereads, selected)
|
|
872
|
+
};
|
|
873
|
+
retrievalFeedbackCache.set(cwd, {
|
|
874
|
+
signature,
|
|
875
|
+
value
|
|
876
|
+
});
|
|
877
|
+
return value;
|
|
878
|
+
}
|
|
879
|
+
async function searchContextIndexV2(cwd, query, options = {}) {
|
|
880
|
+
const excludePatterns = requireExplicitExcludePatterns(options, "searchContextIndexV2");
|
|
881
|
+
const limit = clampInteger(options.limit, 12, 1, 50);
|
|
882
|
+
const projectRoot = fs.realpathSync.native(cwd);
|
|
883
|
+
const directoryCache = /* @__PURE__ */ new Map();
|
|
884
|
+
const status = await contextIndexV2Status(cwd, { excludePatterns });
|
|
885
|
+
if (!status.exists) return {
|
|
886
|
+
query,
|
|
887
|
+
terms: [],
|
|
888
|
+
confidence: "none",
|
|
889
|
+
results: [],
|
|
890
|
+
status
|
|
891
|
+
};
|
|
892
|
+
const terms = tokenizeQuery(query);
|
|
893
|
+
const explicitPaths = queryPathCandidates(query);
|
|
894
|
+
if (terms.length === 0 && explicitPaths.length === 0) return {
|
|
895
|
+
query,
|
|
896
|
+
terms,
|
|
897
|
+
confidence: "none",
|
|
898
|
+
results: [],
|
|
899
|
+
status
|
|
900
|
+
};
|
|
901
|
+
const db = await openDatabase(cwd, { create: false });
|
|
902
|
+
try {
|
|
903
|
+
const scores = /* @__PURE__ */ new Map();
|
|
904
|
+
const pathAllowed = (filePath) => shouldIndexPath(filePath, { excludePatterns });
|
|
905
|
+
const allFiles = db.prepare("SELECT path, mtime_ms FROM files").all().filter((file) => pathAllowed(file.path));
|
|
906
|
+
const explicitRows = [];
|
|
907
|
+
for (const file of allFiles) {
|
|
908
|
+
const normalizedFile = file.path.toLowerCase();
|
|
909
|
+
const basename = path.posix.basename(file.path).toLowerCase();
|
|
910
|
+
if (explicitPaths.some((candidate) => {
|
|
911
|
+
const normalized = candidate.toLowerCase();
|
|
912
|
+
return normalizedFile === normalized || !normalized.includes("/") && basename === normalized;
|
|
913
|
+
})) explicitRows.push({
|
|
914
|
+
path: file.path,
|
|
915
|
+
exact: true
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
addRankedList(scores, explicitRows, "explicit", 3);
|
|
919
|
+
let lexicalRows = [];
|
|
920
|
+
if (terms.length > 0) {
|
|
921
|
+
lexicalRows = db.prepare(`
|
|
922
|
+
SELECT path, bm25(file_fts, 0.0, 1.0, 2.2) lexical_score
|
|
923
|
+
FROM file_fts WHERE file_fts MATCH ? ORDER BY lexical_score LIMIT ?
|
|
924
|
+
`).all(ftsQuery(terms), Math.max(limit * 4, 30)).filter((row) => pathAllowed(row.path));
|
|
925
|
+
addRankedList(scores, lexicalRows, "lexical", 1.5);
|
|
926
|
+
}
|
|
927
|
+
const symbolRows = [];
|
|
928
|
+
const symbolStatement = db.prepare(`
|
|
929
|
+
SELECT file_path, name, kind, line, end_line, signature
|
|
930
|
+
FROM symbols WHERE lower(name) = ? OR lower(name) LIKE ?
|
|
931
|
+
ORDER BY CASE WHEN lower(name) = ? THEN 0 ELSE 1 END, length(name) LIMIT ?
|
|
932
|
+
`);
|
|
933
|
+
for (const term of terms.slice(0, 8)) symbolRows.push(...symbolStatement.all(term, `%${term}%`, term, Math.max(limit * 2, 20)).filter((row) => pathAllowed(row.file_path)));
|
|
934
|
+
addRankedList(scores, [...new Map(symbolRows.map((row) => [`${row.file_path}:${row.line}`, row])).values()], "symbol", 2.2);
|
|
935
|
+
addRankedList(scores, gitChangedFiles(cwd).map((filePath) => ({ path: filePath })).filter((row) => scores.has(row.path)), "git-change", .8);
|
|
936
|
+
addRankedList(scores, allFiles.filter((file) => /(^|\/)(?:test|tests|__tests__)(\/|$)|(?:\.test|\.spec|_test)\./i.test(file.path)).filter((file) => terms.some((term) => file.path.toLowerCase().includes(term))).map((file) => ({ path: file.path })), "test", 1.1);
|
|
937
|
+
addRankedList(scores, retrievalFeedback(cwd).rows.filter((row) => scores.has(row.path)), "feedback", .45);
|
|
938
|
+
const graphSeeds = new Map([...scores.entries()].map(([filePath, value]) => [filePath, value.score]));
|
|
939
|
+
const importRows = db.prepare("SELECT file_path, specifier, target_path FROM imports WHERE target_path IS NOT NULL").all().filter((row) => pathAllowed(row.file_path) && pathAllowed(row.target_path));
|
|
940
|
+
const directLinks = directExplicitImportLinks(importRows, explicitRows);
|
|
941
|
+
addRankedList(scores, directLinks.rankingRows, "import", 1.8);
|
|
942
|
+
addRankedList(scores, personalizedPageRank(importRows, graphSeeds).slice(0, Math.max(limit * 3, 30)), "graph", 1);
|
|
943
|
+
const ranked = [...scores.values()].filter((candidate) => shouldIndexPath(candidate.path, { excludePatterns }) && projectFileInfo(cwd, candidate.path, projectRoot, directoryCache)).sort((left, right) => right.score - left.score).slice(0, limit);
|
|
944
|
+
const symbolLookup = db.prepare("SELECT name, kind, line, end_line, signature FROM symbols WHERE file_path = ? ORDER BY line LIMIT 12");
|
|
945
|
+
const results = ranked.map((candidate) => {
|
|
946
|
+
const matchingSymbols = candidate.rows.filter((row) => row.source === "symbol").map((row) => ({
|
|
947
|
+
name: row.name,
|
|
948
|
+
kind: row.kind,
|
|
949
|
+
line: row.line,
|
|
950
|
+
endLine: row.end_line,
|
|
951
|
+
signature: row.signature
|
|
952
|
+
}));
|
|
953
|
+
const symbols = matchingSymbols.length > 0 ? matchingSymbols : symbolLookup.all(candidate.path).slice(0, 6).map((row) => ({
|
|
954
|
+
name: row.name,
|
|
955
|
+
kind: row.kind,
|
|
956
|
+
line: row.line,
|
|
957
|
+
endLine: row.end_line,
|
|
958
|
+
signature: row.signature
|
|
959
|
+
}));
|
|
960
|
+
return {
|
|
961
|
+
path: candidate.path,
|
|
962
|
+
score: Number(candidate.score.toFixed(6)),
|
|
963
|
+
sources: [...candidate.sources],
|
|
964
|
+
links: directLinks.byPath.get(candidate.path) ?? [],
|
|
965
|
+
symbols
|
|
966
|
+
};
|
|
967
|
+
});
|
|
968
|
+
const top = results[0];
|
|
969
|
+
const exact = Boolean(top?.sources.includes("explicit")) || Boolean(top?.sources.includes("symbol") && top.sources.length >= 2);
|
|
970
|
+
return {
|
|
971
|
+
query,
|
|
972
|
+
terms,
|
|
973
|
+
confidence: !top ? "none" : exact ? "high" : top.sources.length >= 2 ? "medium" : "low",
|
|
974
|
+
results,
|
|
975
|
+
status
|
|
976
|
+
};
|
|
977
|
+
} finally {
|
|
978
|
+
db.close();
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
function readContextTelemetry(cwd, options = {}) {
|
|
982
|
+
const target = contextEnginePaths(cwd).telemetry;
|
|
983
|
+
return readJsonlTail(target, {
|
|
984
|
+
limit: clampInteger(options.limit, MAX_TELEMETRY_EVENTS, 1, MAX_TELEMETRY_EVENTS),
|
|
985
|
+
maxBytes: clampInteger(options.maxBytes, MAX_TELEMETRY_READ_BYTES, 65536, 268435456),
|
|
986
|
+
projectRoot: cwd
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
//#endregion
|
|
990
|
+
//#region ../piagent-core/extensions/workspace-search-index.mjs
|
|
991
|
+
try {
|
|
992
|
+
const result = await searchContextIndexV2(workerData.root, workerData.query, {
|
|
993
|
+
excludePatterns: workerData.excludePatterns,
|
|
994
|
+
limit: 50
|
|
995
|
+
});
|
|
996
|
+
parentPort.postMessage({
|
|
997
|
+
paths: result.results.map((row) => row.path),
|
|
998
|
+
available: result.status.exists
|
|
999
|
+
});
|
|
1000
|
+
} catch {
|
|
1001
|
+
parentPort.postMessage({
|
|
1002
|
+
paths: [],
|
|
1003
|
+
available: false
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
//#endregion
|
|
1007
|
+
export {};
|