@tea-agent/loop-agent 0.39.0-next.9 → 0.39.0
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/AGENTS.md +4 -1
- package/CHANGELOG.md +262 -69
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/dag/generate-task-dag.js +9 -0
- package/dist/application/task-lifecycle/advance.js +4 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +124 -18
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +554 -33
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-failure-category.js +138 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +181 -27
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +28 -0
- package/dist/task/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/deferred-turn.js +91 -0
- package/dist/worker/console/chat/mcp-inventory.js +221 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +165 -83
- package/dist/worker/console/chat/repo-walk.js +10 -1
- package/dist/worker/console/chat/routes.js +480 -55
- package/dist/worker/console/chat/semantic-activity.js +20 -8
- package/dist/worker/console/chat/session-store.js +146 -2
- package/dist/worker/console/chat/turn-process.js +10 -10
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operator-actions.js +195 -4
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +5 -4
- package/dist/worker/console/recovery-cta.js +4 -4
- package/dist/worker/console/recovery-error-copy.js +4 -4
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +42 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-Br92EGb7.js} +1 -1
- package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-oulImtQq.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-tyHc6aqO.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-CY86BSpT.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-BKKsDmNs.js} +1 -1
- package/dist/worker/console/static/assets/channel-TY36zt-i.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-CXZdjl3B.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-B-aIFDQ5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-BDgAW_B2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-DABa4UPR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-aGbysVOx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-B8MSZDVs.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-CpelV73g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-BE04ty3z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-BUxTkaaR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-CYYBVKfX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-CEQF_xmQ.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-CqpxN5IT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-B3PzanVi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-BSUGCNx1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-CdLevU5T.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BjpF_Kok.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C4I6NJny.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-C8hecCr5.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-Daod6tOS.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-BvvmazGv.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-DeN_vfV4.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-D6dq1t9z.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-DziIdNY4.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js} +1 -1
- package/dist/worker/console/static/assets/index-C3GgHg-g.css +1 -0
- package/dist/worker/console/static/assets/index-Ci8ksvBm.js +406 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-Czv5eYx2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-DxGmNj5K.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-CYCFqkO_.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-B0YzRwrd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-t3EZtIGh.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-BTXpTv0T.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-DU8UkKqA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-DP4y8ohp.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-B07vNaYR.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-xDWktb2i.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-BLNMKqeS.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-n0adalxH.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-BbnS-2DL.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-B4uPpCLu.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA--MMbB0fX.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-BXUgp2QY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-CsaMxki7.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-C2IuG9iH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-CmEWOXCW.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-D_9LE0MG.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +1 -1
- package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +74 -26
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +54 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +237 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +50 -22
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +827 -53
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
- package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
- package/dist/workflows/dag/backend-test-intake-context.js +32 -0
- package/dist/workflows/dag/backend-test-layout.js +133 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +167 -32
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
- package/dist/workflows/dag/backend-test-scenario-partitions.js +321 -0
- package/dist/workflows/dag/backend-test-writer-completeness.js +100 -56
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +3 -1
- package/dist/workflows/dag/convergence/controller.js +141 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +25 -9
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-implementation-contract.js +377 -4
- package/dist/workflows/dag/frontend-prewrite-gate.js +96 -84
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +894 -205
- package/dist/workflows/dag/node-execution.js +183 -15
- package/dist/workflows/dag/prompt.js +60 -1
- package/dist/workflows/dag/rerun-feedback.js +212 -0
- package/dist/workflows/dag/rerun-plan.js +224 -27
- package/dist/workflows/dag/rerun-run.js +61 -6
- package/dist/workflows/dag/rerun-task.js +61 -2
- package/dist/workflows/dag/retry-policy.js +109 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +143 -1
- package/dist/workflows/dag/validate.js +64 -20
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +60 -35
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +6 -4
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-BBOTJHvD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/index-B-qWGThd.js +0 -325
- package/dist/worker/console/static/assets/index-C-xRgY_0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +0 -8
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only MCP inventory for Operator Chat.
|
|
3
|
+
*
|
|
4
|
+
* Pi has no built-in MCP catalog. Console projects two real facts:
|
|
5
|
+
* 1. mcp.json server specs (configured, may or may not have registered tools)
|
|
6
|
+
* 2. tools already on the live session that look like MCP registrations
|
|
7
|
+
*
|
|
8
|
+
* Never fabricate servers or tools. Missing files stay empty.
|
|
9
|
+
*/
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
const MCP_CONFIG_NAMES = ["mcp.json"];
|
|
13
|
+
export function isMcpToolName(name) {
|
|
14
|
+
const lower = name.trim().toLowerCase();
|
|
15
|
+
return lower === "mcp" || lower.startsWith("mcp_") || lower.startsWith("mcp-");
|
|
16
|
+
}
|
|
17
|
+
export function inferMcpServerName(toolName, knownServers = []) {
|
|
18
|
+
const name = toolName.trim();
|
|
19
|
+
if (/^mcp$/i.test(name))
|
|
20
|
+
return undefined;
|
|
21
|
+
const rest = name.replace(/^mcp[_-]/i, "");
|
|
22
|
+
if (!rest || rest === name || knownServers.length === 0)
|
|
23
|
+
return undefined;
|
|
24
|
+
const ranked = [...knownServers].sort((left, right) => right.length - left.length);
|
|
25
|
+
const lower = rest.toLowerCase();
|
|
26
|
+
return ranked.find((server) => {
|
|
27
|
+
const key = server.toLowerCase();
|
|
28
|
+
return lower === key || lower.startsWith(`${key}_`) || lower.startsWith(`${key}-`);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function looksLikeMcpTool(tool) {
|
|
32
|
+
const name = typeof tool.name === "string" ? tool.name : "";
|
|
33
|
+
if (!name)
|
|
34
|
+
return false;
|
|
35
|
+
if (isMcpToolName(name))
|
|
36
|
+
return true;
|
|
37
|
+
const source = `${tool.sourceInfo?.source ?? ""} ${tool.sourceInfo?.path ?? ""}`.toLowerCase();
|
|
38
|
+
return source.includes("mcp");
|
|
39
|
+
}
|
|
40
|
+
function asRecord(value) {
|
|
41
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
42
|
+
? value
|
|
43
|
+
: undefined;
|
|
44
|
+
}
|
|
45
|
+
function readEnabled(entry) {
|
|
46
|
+
return entry.enabled !== false;
|
|
47
|
+
}
|
|
48
|
+
function readTransport(entry) {
|
|
49
|
+
if (typeof entry.transport === "string" && entry.transport.trim()) {
|
|
50
|
+
return entry.transport.trim();
|
|
51
|
+
}
|
|
52
|
+
if (typeof entry.type === "string" && entry.type.trim()) {
|
|
53
|
+
return entry.type.trim();
|
|
54
|
+
}
|
|
55
|
+
if (typeof entry.url === "string")
|
|
56
|
+
return "http";
|
|
57
|
+
if (Array.isArray(entry.command) || typeof entry.command === "string") {
|
|
58
|
+
return "stdio";
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function readCommand(entry) {
|
|
63
|
+
if (typeof entry.command === "string" && entry.command.trim()) {
|
|
64
|
+
const args = Array.isArray(entry.args)
|
|
65
|
+
? entry.args.filter((item) => typeof item === "string").join(" ")
|
|
66
|
+
: "";
|
|
67
|
+
return args ? `${entry.command} ${args}` : entry.command;
|
|
68
|
+
}
|
|
69
|
+
if (Array.isArray(entry.command)) {
|
|
70
|
+
return entry.command
|
|
71
|
+
.filter((item) => typeof item === "string")
|
|
72
|
+
.join(" ")
|
|
73
|
+
.trim();
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
function extractServerMap(parsed) {
|
|
78
|
+
const root = asRecord(parsed);
|
|
79
|
+
if (!root)
|
|
80
|
+
return {};
|
|
81
|
+
const mcpServers = asRecord(root.mcpServers);
|
|
82
|
+
if (mcpServers)
|
|
83
|
+
return mcpServers;
|
|
84
|
+
const mcp = asRecord(root.mcp);
|
|
85
|
+
if (mcp)
|
|
86
|
+
return mcp;
|
|
87
|
+
const servers = asRecord(root.servers);
|
|
88
|
+
if (servers)
|
|
89
|
+
return servers;
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
export function parseMcpConfig(raw, scope, configPath) {
|
|
93
|
+
let parsed;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(raw);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const servers = [];
|
|
101
|
+
for (const [name, value] of Object.entries(extractServerMap(parsed))) {
|
|
102
|
+
if (!name.trim())
|
|
103
|
+
continue;
|
|
104
|
+
const entry = asRecord(value) ?? {};
|
|
105
|
+
const command = readCommand(entry);
|
|
106
|
+
const url = typeof entry.url === "string" ? entry.url.trim() : undefined;
|
|
107
|
+
servers.push({
|
|
108
|
+
name: name.trim(),
|
|
109
|
+
scope,
|
|
110
|
+
enabled: readEnabled(entry),
|
|
111
|
+
...(readTransport(entry) ? { transport: readTransport(entry) } : {}),
|
|
112
|
+
...(command ? { command } : {}),
|
|
113
|
+
...(url ? { url } : {}),
|
|
114
|
+
configPath,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return servers;
|
|
118
|
+
}
|
|
119
|
+
export function projectSessionMcpTools(tools, activeToolNames = [], knownServers = []) {
|
|
120
|
+
const active = new Set(activeToolNames.map((name) => name.toLowerCase()));
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
const projected = [];
|
|
123
|
+
for (const tool of tools) {
|
|
124
|
+
if (!looksLikeMcpTool(tool) || typeof tool.name !== "string")
|
|
125
|
+
continue;
|
|
126
|
+
const name = tool.name.trim();
|
|
127
|
+
if (!name || seen.has(name))
|
|
128
|
+
continue;
|
|
129
|
+
seen.add(name);
|
|
130
|
+
const server = inferMcpServerName(name, knownServers);
|
|
131
|
+
projected.push({
|
|
132
|
+
name,
|
|
133
|
+
description: typeof tool.description === "string" ? tool.description : "",
|
|
134
|
+
...(server ? { server } : {}),
|
|
135
|
+
source: tool.sourceInfo?.source ?? "",
|
|
136
|
+
scope: tool.sourceInfo?.scope ?? "session",
|
|
137
|
+
...(tool.sourceInfo?.path ? { path: tool.sourceInfo.path } : {}),
|
|
138
|
+
active: active.has(name.toLowerCase()),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return projected;
|
|
142
|
+
}
|
|
143
|
+
export function assembleMcpInventory(servers, tools) {
|
|
144
|
+
const byServer = new Map();
|
|
145
|
+
const unassigned = [];
|
|
146
|
+
for (const tool of tools) {
|
|
147
|
+
const key = tool.server?.toLowerCase();
|
|
148
|
+
if (!key) {
|
|
149
|
+
unassigned.push(tool);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const list = byServer.get(key) ?? [];
|
|
153
|
+
list.push(tool);
|
|
154
|
+
byServer.set(key, list);
|
|
155
|
+
}
|
|
156
|
+
const views = servers.map((server) => {
|
|
157
|
+
const matched = byServer.get(server.name.toLowerCase()) ?? [];
|
|
158
|
+
byServer.delete(server.name.toLowerCase());
|
|
159
|
+
const status = !server.enabled
|
|
160
|
+
? "disabled"
|
|
161
|
+
: matched.length > 0
|
|
162
|
+
? "loaded"
|
|
163
|
+
: "configured";
|
|
164
|
+
return {
|
|
165
|
+
name: server.name,
|
|
166
|
+
scope: server.scope,
|
|
167
|
+
status,
|
|
168
|
+
enabled: server.enabled,
|
|
169
|
+
...(server.transport ? { transport: server.transport } : {}),
|
|
170
|
+
...(server.command ? { command: server.command } : {}),
|
|
171
|
+
...(server.url ? { url: server.url } : {}),
|
|
172
|
+
configPath: server.configPath,
|
|
173
|
+
tools: matched,
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
for (const [name, matched] of byServer) {
|
|
177
|
+
views.push({
|
|
178
|
+
name,
|
|
179
|
+
scope: "session",
|
|
180
|
+
status: "loaded",
|
|
181
|
+
enabled: true,
|
|
182
|
+
tools: matched,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return { servers: views, unassignedTools: unassigned };
|
|
186
|
+
}
|
|
187
|
+
async function readConfigFile(filePath) {
|
|
188
|
+
try {
|
|
189
|
+
return await readFile(filePath, "utf8");
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
export async function loadMcpServerSpecs(options) {
|
|
196
|
+
const candidates = [
|
|
197
|
+
{ path: path.join(options.agentDir, ...MCP_CONFIG_NAMES), scope: "user" },
|
|
198
|
+
{ path: path.join(options.cwd, ".pi", ...MCP_CONFIG_NAMES), scope: "project" },
|
|
199
|
+
{ path: path.join(options.cwd, ".mcp.json"), scope: "project" },
|
|
200
|
+
];
|
|
201
|
+
const servers = [];
|
|
202
|
+
const seen = new Set();
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
const raw = await readConfigFile(candidate.path);
|
|
205
|
+
if (!raw)
|
|
206
|
+
continue;
|
|
207
|
+
for (const server of parseMcpConfig(raw, candidate.scope, candidate.path)) {
|
|
208
|
+
const key = `${server.scope}:${server.name.toLowerCase()}`;
|
|
209
|
+
if (seen.has(key))
|
|
210
|
+
continue;
|
|
211
|
+
seen.add(key);
|
|
212
|
+
servers.push(server);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return servers;
|
|
216
|
+
}
|
|
217
|
+
export async function readMcpInventory(options) {
|
|
218
|
+
const servers = await loadMcpServerSpecs(options);
|
|
219
|
+
const tools = projectSessionMcpTools(options.tools ?? [], options.activeToolNames ?? [], servers.map((server) => server.name));
|
|
220
|
+
return assembleMcpInventory(servers, tools);
|
|
221
|
+
}
|
|
@@ -64,7 +64,14 @@ function safeRunSummary(value) {
|
|
|
64
64
|
const status = String(node.status ?? "");
|
|
65
65
|
if (!id || !status)
|
|
66
66
|
return [];
|
|
67
|
-
|
|
67
|
+
const failureCategory = String(node.failureCategory ?? "");
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
id,
|
|
71
|
+
status,
|
|
72
|
+
...(failureCategory ? { failureCategory } : {}),
|
|
73
|
+
},
|
|
74
|
+
];
|
|
68
75
|
})
|
|
69
76
|
.slice(0, 20);
|
|
70
77
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { open, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
/** 新建模型时,未显式提供的字段使用的默认值(仅含 6 个定义参数)。 */
|
|
5
|
+
const DEFAULT_MODEL_FIELDS = {
|
|
6
|
+
contextWindow: 32000,
|
|
7
|
+
maxTokens: 8192,
|
|
8
|
+
reasoning: false,
|
|
9
|
+
input: ["text"],
|
|
10
|
+
};
|
|
4
11
|
const revision = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
5
12
|
export async function readModelsConfig(agentDir) {
|
|
6
13
|
let raw = "{}";
|
|
@@ -29,6 +36,18 @@ export async function readModelsConfig(agentDir) {
|
|
|
29
36
|
return {
|
|
30
37
|
id: String(item.id ?? ""),
|
|
31
38
|
name: typeof item.name === "string" ? item.name : undefined,
|
|
39
|
+
contextWindow: typeof item.contextWindow === "number"
|
|
40
|
+
? item.contextWindow
|
|
41
|
+
: undefined,
|
|
42
|
+
maxTokens: typeof item.maxTokens === "number"
|
|
43
|
+
? item.maxTokens
|
|
44
|
+
: undefined,
|
|
45
|
+
input: Array.isArray(item.input)
|
|
46
|
+
? item.input.filter((entry) => typeof entry === "string")
|
|
47
|
+
: undefined,
|
|
48
|
+
reasoning: typeof item.reasoning === "boolean"
|
|
49
|
+
? item.reasoning
|
|
50
|
+
: undefined,
|
|
32
51
|
};
|
|
33
52
|
})
|
|
34
53
|
.filter((model) => model.id)
|
|
@@ -81,19 +100,25 @@ export async function patchModelsConfig(agentDir, input) {
|
|
|
81
100
|
code: "INVALID_INPUT",
|
|
82
101
|
status: 400,
|
|
83
102
|
});
|
|
103
|
+
const fields = validateModelFields(input.model);
|
|
104
|
+
const existingIndex = models.findIndex((model) => model.id === input.model.id);
|
|
105
|
+
const merged = {
|
|
106
|
+
// id/name 在前;新建模型用默认字段补齐(保证定义完整),更新保留已有值。
|
|
107
|
+
id: input.model.id,
|
|
108
|
+
...(fields.name !== undefined ? { name: fields.name } : {}),
|
|
109
|
+
...(existingIndex >= 0 ? models[existingIndex] : DEFAULT_MODEL_FIELDS),
|
|
110
|
+
...fields,
|
|
111
|
+
};
|
|
84
112
|
provider.models =
|
|
85
113
|
input.action === "remove-model"
|
|
86
114
|
? models.filter((model) => model.id !== input.model.id)
|
|
87
|
-
:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: {}),
|
|
95
|
-
},
|
|
96
|
-
];
|
|
115
|
+
: existingIndex >= 0
|
|
116
|
+
? [
|
|
117
|
+
...models.slice(0, existingIndex),
|
|
118
|
+
merged,
|
|
119
|
+
...models.slice(existingIndex + 1),
|
|
120
|
+
]
|
|
121
|
+
: [...models, merged];
|
|
97
122
|
}
|
|
98
123
|
const body = `${JSON.stringify(parsed, null, 2)}\n`;
|
|
99
124
|
const tmp = path.join(agentDir, `.models.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
|
|
@@ -108,6 +133,52 @@ export async function patchModelsConfig(agentDir, input) {
|
|
|
108
133
|
await rename(tmp, file);
|
|
109
134
|
return readModelsConfig(agentDir);
|
|
110
135
|
}
|
|
136
|
+
/** Whitelist + validate model fields carried by a client upsert. Never accept
|
|
137
|
+
* arbitrary keys; only known numeric/boolean/string-array shape fields. */
|
|
138
|
+
function validateModelFields(model) {
|
|
139
|
+
const fields = {};
|
|
140
|
+
if (model.name !== undefined)
|
|
141
|
+
fields.name = String(model.name);
|
|
142
|
+
if (model.contextWindow !== undefined) {
|
|
143
|
+
if (typeof model.contextWindow !== "number" || model.contextWindow < 0)
|
|
144
|
+
throw invalid("contextWindow must be a non-negative number");
|
|
145
|
+
fields.contextWindow = model.contextWindow;
|
|
146
|
+
}
|
|
147
|
+
if (model.maxTokens !== undefined) {
|
|
148
|
+
if (typeof model.maxTokens !== "number" || model.maxTokens < 0)
|
|
149
|
+
throw invalid("maxTokens must be a non-negative number");
|
|
150
|
+
fields.maxTokens = model.maxTokens;
|
|
151
|
+
}
|
|
152
|
+
if (model.reasoning !== undefined) {
|
|
153
|
+
if (typeof model.reasoning !== "boolean")
|
|
154
|
+
throw invalid("reasoning must be a boolean");
|
|
155
|
+
fields.reasoning = model.reasoning;
|
|
156
|
+
}
|
|
157
|
+
if (model.input !== undefined) {
|
|
158
|
+
if (!Array.isArray(model.input) ||
|
|
159
|
+
!model.input.every((item) => typeof item === "string"))
|
|
160
|
+
throw invalid("input must be an array of strings");
|
|
161
|
+
fields.input = model.input.map((item) => String(item));
|
|
162
|
+
}
|
|
163
|
+
if (model.cost !== undefined) {
|
|
164
|
+
if (!model.cost || typeof model.cost !== "object")
|
|
165
|
+
throw invalid("cost must be an object");
|
|
166
|
+
const cost = {};
|
|
167
|
+
for (const key of ["input", "output", "cacheRead", "cacheWrite"]) {
|
|
168
|
+
const value = model.cost[key];
|
|
169
|
+
if (value !== undefined) {
|
|
170
|
+
if (typeof value !== "number" || value < 0)
|
|
171
|
+
throw invalid(`cost.${key} must be a non-negative number`);
|
|
172
|
+
cost[key] = value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
fields.cost = cost;
|
|
176
|
+
}
|
|
177
|
+
return fields;
|
|
178
|
+
}
|
|
179
|
+
function invalid(message) {
|
|
180
|
+
return Object.assign(new Error(message), { code: "INVALID_INPUT", status: 400 });
|
|
181
|
+
}
|
|
111
182
|
/** Max bytes of a package.json we are willing to read (read-only, bounded). */
|
|
112
183
|
export const MAX_PACKAGE_JSON_BYTES = 64 * 1024;
|
|
113
184
|
/** Max directory levels walked upward from an owned resolved resource path. */
|