deepdebug-local-agent 1.0.23 → 1.2.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/.claude/context.md +76 -0
- package/.claude/skills/README.md +25 -0
- package/.claude/skills/tdd-debugging/SKILL.md +53 -0
- package/.claude/skills/tdd-debugging/references/anti-patterns.md +43 -0
- package/.claude/skills/tdd-debugging/references/command-reference.md +46 -0
- package/.claude/standards/README.md +51 -0
- package/.claude/standards/clean-code.md +75 -0
- package/.claude/standards/code-review-checklist.md +61 -0
- package/.claude/standards/layered-architecture.md +163 -0
- package/.claude/standards/node.md +45 -0
- package/.claude/standards/security-owasp.md +114 -0
- package/.claude/standards/simplicity.md +42 -0
- package/.claude/standards/solid.md +64 -0
- package/.claude/standards/testing.md +59 -0
- package/.cursor/rules/standards.mdc +17 -0
- package/.github/workflows/build-byoc-image.yml +315 -0
- package/.github/workflows/deploy-gcp.yml +102 -0
- package/.github/workflows/npm-publish.yml +76 -8
- package/AGENTS.md +21 -0
- package/CHANGELOG.md +76 -0
- package/CLAUDE.md +13 -0
- package/CLAUDE_CODE_AGENT_INFINITE.md +5 -3
- package/Dockerfile +4 -0
- package/Dockerfile.full +55 -3
- package/bin/install.js +16 -5
- package/cloudbuild-full.yaml +57 -0
- package/docker-compose.yml +5 -3
- package/docs/OBSERVABILITY.md +164 -0
- package/docs/byoc-agent-image.md +70 -0
- package/docs/dlp.md +78 -0
- package/docs/graphify-packaging-plan.md +133 -0
- package/docs/graphify-validation-run-2026-10-28.md +243 -0
- package/docs/releasing.md +37 -0
- package/docs/signed-commands.md +163 -0
- package/env.example +34 -0
- package/helm/Chart.yaml +2 -2
- package/helm/templates/deployment.yaml +13 -3
- package/helm/templates/networkpolicy.yaml +22 -0
- package/helm/templates/secret.yaml +1 -0
- package/helm/values.yaml +13 -14
- package/mcp-server.js +4 -1
- package/package.json +9 -4
- package/scripts/next-version.mjs +75 -0
- package/scripts/upgrade-observability-push.sh +227 -0
- package/src/ai-engine.js +22 -22
- package/src/config/trusted-signing-keys.json +3 -0
- package/src/detectors/build-tool-detector.js +50 -0
- package/src/detectors/multi-project-detector.js +22 -22
- package/src/detectors/service-detector.js +24 -24
- package/src/dlp/engine.js +111 -0
- package/src/dlp/index.js +137 -0
- package/src/dlp/rules.js +97 -0
- package/src/dlp/validators.js +123 -0
- package/src/exec-utils.js +26 -4
- package/src/git/base-git-provider.js +117 -117
- package/src/git/bitbucket-provider.js +2 -2
- package/src/git/git-provider-registry.js +18 -18
- package/src/git/github-provider.js +13 -13
- package/src/git/multi-repo-pr.js +161 -0
- package/src/mcp-http-server.js +32 -16
- package/src/observability/bundle-files.js +418 -0
- package/src/observability/detect.js +151 -0
- package/src/observability/provision.js +212 -0
- package/src/observability/render-cli.js +59 -0
- package/src/observability/routes.js +88 -0
- package/src/patch/patch-engine.js +28 -28
- package/src/patch-manager.js +85 -85
- package/src/patch_bkp.js +14 -14
- package/src/ports.js +4 -4
- package/src/routes/workspace.route.js +47 -16
- package/src/runtimes/base-runtime.js +75 -75
- package/src/runtimes/java/gradle-runtime.js +17 -17
- package/src/runtimes/java/java-integrations.js +25 -25
- package/src/runtimes/java/maven-runtime.js +34 -34
- package/src/runtimes/node/node-integrations.js +1 -1
- package/src/runtimes/node/npm-runtime.js +14 -14
- package/src/runtimes/node/yarn-runtime.js +4 -4
- package/src/runtimes/runtime-registry.js +25 -25
- package/src/server-local.js +4 -4
- package/src/server.js +1366 -333
- package/src/siblings/burst-suppressor.js +89 -0
- package/src/siblings/capabilities.js +273 -0
- package/src/siblings/env-compat.cjs +58 -0
- package/src/siblings/incident-forwarder.js +322 -0
- package/src/siblings/logger.js +15 -0
- package/src/siblings/metrics.js +94 -0
- package/src/utils/agent-auth.js +184 -0
- package/src/utils/approval-token.js +56 -0
- package/src/utils/bind-host.js +29 -0
- package/src/utils/branch-refspec.js +98 -0
- package/src/utils/child-env.js +34 -0
- package/src/utils/environment-diagnostics.js +55 -55
- package/src/utils/exec-policy.js +962 -0
- package/src/utils/exec-utils.js +15 -15
- package/src/utils/file-count.js +46 -0
- package/src/utils/fs-utils.js +10 -10
- package/src/utils/multi-repo-open.js +132 -0
- package/src/utils/open-gate.js +183 -0
- package/src/utils/ranged-read.js +65 -0
- package/src/utils/request-guards.js +77 -0
- package/src/utils/request-signing.js +342 -0
- package/src/utils/root-recovery.js +244 -0
- package/src/utils/safe-exec-args.js +58 -0
- package/src/utils/sandbox.js +182 -0
- package/src/utils/search-outcome.js +56 -0
- package/src/utils/workspace-root.js +80 -0
- package/src/validators/syntax-validator.js +149 -0
- package/src/workspace/detect-port.js +16 -16
- package/src/workspace/file-reader.js +1 -1
- package/src/workspace/process-manager.js +46 -46
- package/src/workspace-manager.js +86 -24
- package/test/adapters/registry.test.js +9 -2
- package/test/agent-auth.test.js +194 -0
- package/test/bind-host.test.js +26 -0
- package/test/branch-refspec.test.js +224 -0
- package/test/burst-suppressor.test.js +60 -0
- package/test/capabilities-access.test.js +47 -0
- package/test/capabilities.test.js +87 -0
- package/test/child-env.test.js +23 -0
- package/test/dlp/engine.test.js +173 -0
- package/test/dlp/fixtures.js +126 -0
- package/test/dlp/index.test.js +166 -0
- package/test/env-compat.test.js +71 -0
- package/test/exec-policy.test.js +463 -0
- package/test/file-count.test.js +64 -0
- package/test/incident-forwarder.test.js +231 -0
- package/test/multi-repo-open.test.js +180 -0
- package/test/multi-repo-pr.test.js +129 -0
- package/test/next-version.test.js +48 -0
- package/test/observability-detect.test.js +199 -0
- package/test/open-gate.test.js +341 -0
- package/test/ranged-read.test.js +60 -0
- package/test/request-guards.test.js +91 -0
- package/test/request-signing.test.js +284 -0
- package/test/root-recovery-sidecar.test.js +171 -0
- package/test/root-recovery.test.js +274 -0
- package/test/safe-exec-args.test.js +58 -0
- package/test/sandbox.test.js +98 -0
- package/test/search-outcome.test.js +50 -0
- package/test/workspace-root.test.js +64 -0
- package/tools/graphify-validation/README.md +92 -0
- package/tools/graphify-validation/config/questions.json +31 -0
- package/tools/graphify-validation/config/repos.json +9 -0
- package/tools/graphify-validation/config/symbols.json +6 -0
- package/tools/graphify-validation/fixtures/spring/expected.json +34 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AppConfig.java +29 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AuditLogger.java +15 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/Cache.java +7 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/CheckoutService.java +25 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/InMemoryCache.java +18 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/OrderController.java +22 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaymentGateway.java +5 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaypalGateway.java +11 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/RefundService.java +14 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/StripeGateway.java +11 -0
- package/tools/graphify-validation/lib/affected_vs_rg.py +81 -0
- package/tools/graphify-validation/lib/audit_hook/sitecustomize.py +22 -0
- package/tools/graphify-validation/lib/graph_metrics.py +96 -0
- package/tools/graphify-validation/lib/gv_common.py +130 -0
- package/tools/graphify-validation/lib/licenses.py +47 -0
- package/tools/graphify-validation/lib/pip_audit_summary.py +18 -0
- package/tools/graphify-validation/lib/recall.py +115 -0
- package/tools/graphify-validation/lib/report.py +226 -0
- package/tools/graphify-validation/lib/run_all.py +237 -0
- package/tools/graphify-validation/lib/run_graphify.py +144 -0
- package/tools/graphify-validation/lib/rusage_helper.py +17 -0
- package/tools/graphify-validation/lib/sample_edges.py +246 -0
- package/tools/graphify-validation/lib/spring_fixture.py +81 -0
- package/tools/graphify-validation/requirements-py311.lock +508 -0
- package/tools/graphify-validation/requirements-tools.in +1 -0
- package/tools/graphify-validation/requirements-tools.lock +371 -0
- package/tools/graphify-validation/requirements.in +1 -0
- package/tools/graphify-validation/requirements.lock +502 -0
- package/tools/graphify-validation/run.sh +43 -0
- package/tools/graphify-validation/tests/test_harness.py +151 -0
- package/cloudbuild-agent-qa.yaml +0 -43
- package/cloudbuild-agent-staging.yaml +0 -40
- package/cloudbuild.yaml.deprecated +0 -42
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Graphify: corrida do harness de validação, 2026-10-28
|
|
2
|
+
|
|
3
|
+
Corrida real de `tools/graphify-validation/run.sh --pip-audit` (T4-06, D8) contra `vh-api-gateway@release/qa`, `visionhammer-agents@release/qa` e
|
|
4
|
+
`deepdebug-local-agent@main` (SHAs na secção 0 do relatório). Máquina de desenvolvimento (macOS arm64, 10 CPUs, 16 GB), **não** um VPS de 2 vCPU / 4 GB.
|
|
5
|
+
Graphify `graphifyy==0.9.93` do PyPI, instalado com `--require-hashes`. Sem LLM, sem rede para o Graphify, sem acesso ao pgvector de PROD nem de QA.
|
|
6
|
+
|
|
7
|
+
**Conclusão curta: o Graphify continua "go com condições", e "Graphify validado" (D5) ainda não pode ser declarado.** Dos limiares propostos, nesta corrida
|
|
8
|
+
falham G1 (só no repo do Local Agent), G3, G4 (memória no incremental), G5 e G8 (versionamento no agents); passam os testes de rede e determinismo; G6, G9 e G10 não foram medidos.
|
|
9
|
+
|
|
10
|
+
## Leitura dos resultados (com as ressalvas que alteram a conclusão)
|
|
11
|
+
|
|
12
|
+
- **G1, extração.** gateway 99,81 % (3 ficheiros sem símbolos), agents 100 %, Local Agent 93,33 % (8 de 120: 7 são testes `node:test` só com código de topo, mais `src/workspace/git-client.js`, **causa não investigada**). Falha o limiar de 95 % só no Local Agent, num repo pequeno onde um ficheiro pesa 0,8 %.
|
|
13
|
+
- **G2, arestas.** INFERRED: gateway 12,22 %, agents 14,05 % (a 1 ponto do limiar de 15 %; a nota do piloto tinha 11,4 % e 13,6 % com a 0.9.86), Local Agent 1,68 %. A precisão da amostra vem de um **oráculo automático** (tipo declarado do recetor), não de revisão humana: gateway 94,6 % nas 56 decididas de 60, agents 100 % nas 53 decididas de 60, Local Agent 100 % em 7 (só 7 arestas INFERRED `calls` fora de testes). O oráculo reproduziu o falso positivo que o piloto cita (`webClientBuilder.build()` apontado para `RootAuditEvent.Builder`, 3 ocorrências na amostra do gateway), o que mostra que não aprova tudo. Fica `PARCIAL`: falta a revisão por 2 pessoas e as linhas `unknown` (construtores, chamadas encadeadas, `var`) estão por julgar nos CSV.
|
|
14
|
+
- **G3, Spring/DI (fixture sintética, 19 verificações).** Falha: interface→implementação 62,5 % (5/8). O grafo liga `StripeGateway implements PaymentGateway` e as chamadas à interface, mas **não liga quem chama a interface às implementações** (3 verificações "chega à implementação" falham). Injeção por Lombok/construtor por tipo: 4/4. `@Bean`: 2/4 (o método `@Bean` aparece como método comum; quem consome o bean não chega ao método). `@Qualifier`: 0/3 (ignorado). Confirma a condição do piloto "sem nós de bean". Consequência para o loop: o contexto de "quem é afetado por mudar `PaymentGateway.charge`" tem de juntar `implements` a mão (ou o fallback `rg`).
|
|
15
|
+
- **G4, desempenho.** A frio: gateway 19,5 s, agents 15,3 s, Local Agent 2,8 s, todos abaixo de 60 s; maior processo filho 440-445 MB (estimativa da soma da árvore: ~795 MB, que conta páginas partilhadas). **Incremental (1 ficheiro): 21,6 s e 18,7 s, maior filho 1,17 e 1,13 GB, acima do limiar de 1 GB** (o piloto tinha 1,06 GB). Confirma que o `update` incremental reconstrói tudo. Em 2 vCPU/4 GB **não medido**; o tempo deve subir, a memória do maior processo não deve descer.
|
|
16
|
+
- **G5, recall.**
|
|
17
|
+
- "Quem é afetado" (`affected`, profundidade 1, só fontes main, `rg -w` como referência): recall agregado 54,8 % no gateway, 76,8 % no agents, 87,5 % no Local Agent; o limiar é 90 %. A precisão contra `rg` ficou em 100 % no gateway e no Local Agent e 79,6 % no agents. Os piores casos são classes cujo uso é por métodos (`PolicyGate`: 10 % a profundidade 1, 15 % a profundidade 2): o nó da classe não recebe as chamadas aos seus métodos. Ou seja, a primitiva certa é `affected` **por método**, não por classe, e isso não foi medido aqui.
|
|
18
|
+
- recall@8 com 22 perguntas escritas à mão (`config/questions.json`; 8 gateway, 8 agents, 6 Local Agent): o grafo fica em 43,8 % (gateway), 62,5 % (agents) e 66,7 % (Local Agent). O baseline lexical **justo** (palavras tiradas só do texto da pergunta, `rg` insensível a maiúsculas) dá 87,5 %, 75,0 % e 91,7 %. As perguntas conceptuais sem nome de símbolo são o ponto fraco do grafo (gateway 25 %, agents 57,1 %), exatamente a lacuna que o piloto não tinha testado e que o D14 (fallback ripgrep) cobre. As perguntas que nomeiam o símbolo: grafo 100 %.
|
|
19
|
+
- A coluna `lexical_curated` (100 % nos três) usa palavras escolhidas por quem já sabia a resposta; **não é comparável** e está no relatório só como teto.
|
|
20
|
+
- **pgvector não medido**: sem o dataset do `RagEvalController` e sem BD, a comparação "grafo ≥ pgvector − 5 pp" do G5 não pode ser feita. O harness aceita `--vector-results` para quando houver.
|
|
21
|
+
- O conjunto de 22 perguntas é pequeno e a verdade (ficheiros) foi verificada à mão por um só agente; serve para comparar backends, não para um número absoluto.
|
|
22
|
+
- **G7, segurança.** Com a rede negada (`sandbox-exec`, verificado por uma sonda TCP que falhou), os 3 repos terminaram com exit 0, **0 eventos `socket.*`** no hook de auditoria e só 2 `subprocess.Popen` (`git rev-parse HEAD`). `pip-audit`: 0 vulnerabilidades conhecidas nos 32 pacotes do lock 3.12 (não foi corrido no lock 3.11). Licenças: 32 permissivas, `tree-sitter-groovy` sem ficheiro de licença no wheel. `PARCIAL`: a lista de comandos desativados é requisito do wrapper, não foi testada.
|
|
23
|
+
- **G8, higiene.** Determinismo: duas corridas a frio dão a mesma impressão de nós/arestas nos 3 repos. `visionhammer-agents@release/qa` **tem 57 ficheiros `graphify-out/` versionados** (gateway e Local Agent: 0). O limiar exige 0, logo falha até haver o PR de remoção.
|
|
24
|
+
- **Versão.** O piloto mediu o fork 0.9.86; esta corrida mede o PyPI 0.9.93. Números próximos (tempos, INFERRED, grafos de ~17 000 nós), sem sinal de que o fork acrescente valor, o que sustenta D8.
|
|
25
|
+
|
|
26
|
+
## Não medido (e porquê)
|
|
27
|
+
|
|
28
|
+
| Item | Motivo |
|
|
29
|
+
|---|---|
|
|
30
|
+
| recall@8 contra pgvector (G5) | sem BD de QA nem dataset do `RagEvalController` |
|
|
31
|
+
| G6, incidentes ponta a ponta sem Voyage | exige agent2/LA com LLM |
|
|
32
|
+
| G9, imagem `local-agent-full` | sem Docker local; plano em `docs/graphify-packaging-plan.md` |
|
|
33
|
+
| G10, Python, SQL, Go, Terraform reais e repo do piloto | fora dos 3 repos pedidos; o piloto anterior só tinha fixtures sintéticas para Go e Terraform |
|
|
34
|
+
| 2 vCPU / 4 GB e hardware do piloto | só há esta máquina |
|
|
35
|
+
| Revisão manual de 2 pessoas das INFERRED | passo humano; CSV pronto |
|
|
36
|
+
| `pip-audit` do lock Python 3.11, instalação do lock em Linux | só macOS arm64 e lock 3.12 auditados |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Relatório gerado (REPORT.md, sem edição manual)
|
|
41
|
+
|
|
42
|
+
Gerado por `tools/graphify-validation/run.sh`. Marcacao: PASSA/FALHA contra o limiar proposto; PARCIAL = medido mas falta um passo humano ou de ambiente; NAO MEDIDO = nao deu para medir aqui.
|
|
43
|
+
|
|
44
|
+
## 0. Ambiente e versoes
|
|
45
|
+
|
|
46
|
+
| Item | Valor |
|
|
47
|
+
|---|---|
|
|
48
|
+
| Data (UTC) | 2026-10-28T03:57:56Z |
|
|
49
|
+
| Graphify | graphifyy==0.9.93 (fixado por hash) |
|
|
50
|
+
| Python | Python 3.12.10 |
|
|
51
|
+
| SO / CPU / RAM | Darwin 25.3.0 arm64 / 10 CPUs / 16.0 GB |
|
|
52
|
+
| ripgrep | ripgrep 14.1.1 (rev 0ae49f8b6e) |
|
|
53
|
+
| Bloqueio de rede | sandbox-exec (deny network*); eficaz: True |
|
|
54
|
+
| Ambiente do processo | so HOME, LANG, NO_COLOR, PATH, PYTHONHASHSEED (sem chaves LLM nem credenciais) |
|
|
55
|
+
|
|
56
|
+
| Repo | Ref | SHA | Ficheiros de codigo |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| deepdebug-local-agent | origin/main | 854fa34cb8 | 120 |
|
|
59
|
+
| vh-api-gateway | origin/release/qa | ef42fba928 | 1552 |
|
|
60
|
+
| visionhammer-agents | origin/release/qa | 33a705cc9a | 1056 |
|
|
61
|
+
|
|
62
|
+
## 1. Resumo por criterio
|
|
63
|
+
|
|
64
|
+
| ID | Veredito | Medido |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| G1 | FALHA | deepdebug-local-agent 93.33% (8 sem simbolos); vh-api-gateway 99.81% (3 sem simbolos); visionhammer-agents 100.0% (0 sem simbolos) |
|
|
67
|
+
| G2 | PARCIAL | deepdebug-local-agent INFERRED 1.68%, precisao oraculo 100.0% (n=7, decididas 7); vh-api-gateway INFERRED 12.22%, precisao oraculo 94.6% (n=60, decididas 56); visionhammer-agents INFERRED 14.05%, precisao oraculo 100.0% (n=60, decididas 53); falta revisao manual por 2 pessoas |
|
|
68
|
+
| G3 | FALHA | interface->impl 62.5% (limiar 90%) |
|
|
69
|
+
| G4 | FALHA | deepdebug-local-agent frio 2.83s, arvore 635.4 MB, maior filho 94.6 MB; incremental 2.8s, arvore 635.7 MB, maior filho 121.0 MB; vh-api-gateway frio 19.47s, arvore 795.9 MB, maior filho 444.2 MB; incremental 21.64s, arvore 1093.5 MB, maior filho 1174.2 MB; visionhammer-agents frio 15.27s, arvore 793.7 MB, maior filho 439.7 MB; incremental 18.68s, arvore 1123.9 MB, maior filho 1134.7 MB; maquina de desenvolvimento (nao 2 vCPU/4 GB); soma de RSS da arvore acima de 1 GB em pelo menos uma corrida (pode contar paginas partilhadas) |
|
|
70
|
+
| G5 | FALHA | deepdebug-local-agent affected recall vs rg 87.5% (precisao 100.0%); deepdebug-local-agent recall@8 grafo 66.7% vs lexical (palavras da pergunta) 91.7%; vh-api-gateway affected recall vs rg 54.8% (precisao 100.0%); vh-api-gateway recall@8 grafo 43.8% vs lexical (palavras da pergunta) 87.5%; visionhammer-agents affected recall vs rg 76.8% (precisao 79.6%); visionhammer-agents recall@8 grafo 62.5% vs lexical (palavras da pergunta) 75.0%; pgvector NAO medido (sem BD de QA) |
|
|
71
|
+
| G6 | NAO MEDIDO | exige agent2/LA com LLM e incidentes reais; fora do harness |
|
|
72
|
+
| G7 | PARCIAL | deepdebug-local-agent: 0 eventos socket, exit 0; vh-api-gateway: 0 eventos socket, exit 0; visionhammer-agents: 0 eventos socket, exit 0; pip-audit: 0 vulnerabilidades conhecidas em 32 pacotes (exit 0); comandos de rede desativados = requisito de empacotamento, nao testado aqui |
|
|
73
|
+
| G8 | FALHA | determinismo igual em 3/3 repos; deepdebug-local-agent: 0 ficheiros graphify-out versionados; vh-api-gateway: 0 ficheiros graphify-out versionados; visionhammer-agents: 57 ficheiros graphify-out versionados |
|
|
74
|
+
| G9 | NAO MEDIDO | sem Docker local; ver docs/graphify-packaging-plan.md |
|
|
75
|
+
| G10 | NAO MEDIDO | Java e JS cobertos por repos VH; Python/SQL/Go/Terraform e repo do piloto fora deste conjunto |
|
|
76
|
+
|
|
77
|
+
## 2. Detalhe por repo
|
|
78
|
+
|
|
79
|
+
### deepdebug-local-agent
|
|
80
|
+
|
|
81
|
+
- Corrida a frio: 2.83s, pico de RSS da arvore 635.4 MB (estimativa; maior filho 94.6 MB), nos 1642, arestas 3283, graphify-out 3.2 MB.
|
|
82
|
+
- G1: 112/120 ficheiros de codigo com simbolos (93.33%); 8 sem simbolos, dos quais 7 em testes. Lista: src/workspace/git-client.js, test/bind-host.test.js, test/child-env.test.js, test/file-count.test.js, test/observability-detect.test.js, test/sandbox.test.js, test/search-outcome.test.js, test/adapters/registry.test.js
|
|
83
|
+
- G2: INFERRED 1.68% (1.69% so em fontes main); INFERRED por relacao {'calls': 7, 'indirect_call': 38, 'references': 10}.
|
|
84
|
+
- G8 determinismo: assinatura aab33c2415660429 vs aab33c2415660429, igual: True.
|
|
85
|
+
- Incremental (1 ficheiro alterado: bin/install.js): 2.8s, 635.7 MB; simbolo novo no grafo: True; sem alteracoes: 2.38s.
|
|
86
|
+
- G7 rede bloqueada (sandbox-exec (deny network*)): exit 0, 0 eventos socket, 2 subprocess.Popen (('git', ['git', 'rev-parse', 'HEAD'], '/private/tmp/claude-501/-Users-).
|
|
87
|
+
- G2 amostra (seed 20261028, n=7): oraculo ok/wrong/unknown {'ok': 7}; precisao nas decididas 100.0%; estrato ambiguo {'n': 0, 'ok': 0, 'precision_pct': None, 'unknown': 0, 'wrong': 0}; estrato outros {'n': 7, 'ok': 7, 'precision_pct': 100.0, 'unknown': 0, 'wrong': 0}. Revisao manual: 0 linhas.
|
|
88
|
+
|
|
89
|
+
`affected` (profundidade 1) contra `rg -w`, so fontes main:
|
|
90
|
+
|
|
91
|
+
| Simbolo | grafo | rg | ambos | so grafo | so rg | recall vs rg | precisao vs rg |
|
|
92
|
+
|---|---|---|---|---|---|---|---|
|
|
93
|
+
| confineToRoot (ok) | 2 | 3 | 2 | 0 | 1 | 66.7% | 100.0% |
|
|
94
|
+
| sanitizedChildEnv (ok) | 2 | 2 | 2 | 0 | 0 | 100.0% | 100.0% |
|
|
95
|
+
| verifySignature (ok) | 0 | 0 | 0 | 0 | 0 | n/d | n/d |
|
|
96
|
+
| evaluateCommand (ok) | 0 | 0 | 0 | 0 | 0 | n/d | n/d |
|
|
97
|
+
| resolveBindHost (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
98
|
+
| validateHeaderRoot (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
99
|
+
| BurstSuppressor (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
100
|
+
| isSecretEnvName (ok) | 0 | 0 | 0 | 0 | 0 | n/d | n/d |
|
|
101
|
+
|
|
102
|
+
Agregado (profundidade 1): recall 87.5%, precisao 100.0% (5 simbolos); profundidade 2: recall 87.5%.
|
|
103
|
+
|
|
104
|
+
recall@8 no conjunto de referencia (6 perguntas):
|
|
105
|
+
|
|
106
|
+
| Backend | Tipo | n | recall@8 | acerto@8 |
|
|
107
|
+
|---|---|---|---|---|
|
|
108
|
+
| graph | all | 6 | 66.7% | 66.7% |
|
|
109
|
+
| graph | conceptual | 6 | 66.7% | 66.7% |
|
|
110
|
+
| graph_call_only | all | 6 | 66.7% | 66.7% |
|
|
111
|
+
| graph_call_only | conceptual | 6 | 66.7% | 66.7% |
|
|
112
|
+
| lexical_auto | all | 6 | 91.7% | 100.0% |
|
|
113
|
+
| lexical_auto | conceptual | 6 | 91.7% | 100.0% |
|
|
114
|
+
| lexical_curated | all | 6 | 100.0% | 100.0% |
|
|
115
|
+
| lexical_curated | conceptual | 6 | 100.0% | 100.0% |
|
|
116
|
+
|
|
117
|
+
`lexical_auto` usa so o texto da pergunta (baseline justo para o fallback ripgrep); `lexical_curated` usa palavras escolhidas por quem conhecia a resposta (teto otimista, nao comparar).
|
|
118
|
+
| vector (pgvector) | - | - | NAO MEDIDO | NAO MEDIDO |
|
|
119
|
+
|
|
120
|
+
### vh-api-gateway
|
|
121
|
+
|
|
122
|
+
- Corrida a frio: 19.47s, pico de RSS da arvore 795.9 MB (estimativa; maior filho 444.2 MB), nos 17654, arestas 71062, graphify-out 133.0 MB.
|
|
123
|
+
- G1: 1549/1552 ficheiros de codigo com simbolos (99.81%); 3 sem simbolos, dos quais 0 em testes. Lista: src/main/java/io/visionhammer/gateway/filter/RequestLoggingFilter.java, src/main/java/io/visionhammer/gateway/logs/api/LogsController.java, src/main/java/io/visionhammer/gateway/security/bo/package-info.java
|
|
124
|
+
- G2: INFERRED 12.22% (8.86% so em fontes main); INFERRED por relacao {'calls': 8263, 'references': 423}.
|
|
125
|
+
- G8 determinismo: assinatura 0bbbea2d3be0dc3d vs 0bbbea2d3be0dc3d, igual: True.
|
|
126
|
+
- Incremental (1 ficheiro alterado: src/main/java/io/visionhammer/gateway/admin/api/AdminInsightsController.java): 21.64s, 1093.5 MB; simbolo novo no grafo: True; sem alteracoes: 12.98s.
|
|
127
|
+
- G7 rede bloqueada (sandbox-exec (deny network*)): exit 0, 0 eventos socket, 2 subprocess.Popen (('git', ['git', 'rev-parse', 'HEAD'], '/private/tmp/claude-501/-Users-).
|
|
128
|
+
- G2 amostra (seed 20261028, n=60): oraculo ok/wrong/unknown {'ok': 53, 'unknown': 4, 'wrong': 3}; precisao nas decididas 94.6%; estrato ambiguo {'n': 20, 'ok': 16, 'precision_pct': 84.2, 'unknown': 1, 'wrong': 3}; estrato outros {'n': 40, 'ok': 37, 'precision_pct': 100.0, 'unknown': 3, 'wrong': 0}. Revisao manual: 0 linhas.
|
|
129
|
+
|
|
130
|
+
`affected` (profundidade 1) contra `rg -w`, so fontes main:
|
|
131
|
+
|
|
132
|
+
| Simbolo | grafo | rg | ambos | so grafo | so rg | recall vs rg | precisao vs rg |
|
|
133
|
+
|---|---|---|---|---|---|---|---|
|
|
134
|
+
| TenantAccessGuard (ok) | 21 | 21 | 21 | 0 | 0 | 100.0% | 100.0% |
|
|
135
|
+
| PolicyGate (ok) | 4 | 40 | 4 | 0 | 36 | 10.0% | 100.0% |
|
|
136
|
+
| EgressUrlGuard (ok) | 11 | 13 | 11 | 0 | 2 | 84.6% | 100.0% |
|
|
137
|
+
| SlackRequestVerifier (ok) | 2 | 6 | 2 | 0 | 4 | 33.3% | 100.0% |
|
|
138
|
+
| JwtTokenProvider (ok) | 8 | 8 | 8 | 0 | 0 | 100.0% | 100.0% |
|
|
139
|
+
| AuthRateLimiter (ok) | 4 | 5 | 4 | 0 | 1 | 80.0% | 100.0% |
|
|
140
|
+
| DataErasureService (ok) | 0 | 2 | 0 | 0 | 2 | 0.0% | n/d |
|
|
141
|
+
| EmailService (ok) | 7 | 9 | 7 | 0 | 2 | 77.8% | 100.0% |
|
|
142
|
+
|
|
143
|
+
Agregado (profundidade 1): recall 54.8%, precisao 100.0% (8 simbolos); profundidade 2: recall 57.7%.
|
|
144
|
+
|
|
145
|
+
recall@8 no conjunto de referencia (8 perguntas):
|
|
146
|
+
|
|
147
|
+
| Backend | Tipo | n | recall@8 | acerto@8 |
|
|
148
|
+
|---|---|---|---|---|
|
|
149
|
+
| graph | all | 8 | 43.8% | 50.0% |
|
|
150
|
+
| graph | conceptual | 6 | 25.0% | 33.3% |
|
|
151
|
+
| graph | symbolic | 2 | 100.0% | 100.0% |
|
|
152
|
+
| graph_call_only | all | 8 | 43.8% | 50.0% |
|
|
153
|
+
| graph_call_only | conceptual | 6 | 25.0% | 33.3% |
|
|
154
|
+
| graph_call_only | symbolic | 2 | 100.0% | 100.0% |
|
|
155
|
+
| lexical_auto | all | 8 | 87.5% | 87.5% |
|
|
156
|
+
| lexical_auto | conceptual | 6 | 100.0% | 100.0% |
|
|
157
|
+
| lexical_auto | symbolic | 2 | 50.0% | 50.0% |
|
|
158
|
+
| lexical_curated | all | 8 | 100.0% | 100.0% |
|
|
159
|
+
| lexical_curated | conceptual | 6 | 100.0% | 100.0% |
|
|
160
|
+
| lexical_curated | symbolic | 2 | 100.0% | 100.0% |
|
|
161
|
+
|
|
162
|
+
`lexical_auto` usa so o texto da pergunta (baseline justo para o fallback ripgrep); `lexical_curated` usa palavras escolhidas por quem conhecia a resposta (teto otimista, nao comparar).
|
|
163
|
+
| vector (pgvector) | - | - | NAO MEDIDO | NAO MEDIDO |
|
|
164
|
+
|
|
165
|
+
### visionhammer-agents
|
|
166
|
+
|
|
167
|
+
- Corrida a frio: 15.27s, pico de RSS da arvore 793.7 MB (estimativa; maior filho 439.7 MB), nos 16776, arestas 60583, graphify-out 121.6 MB.
|
|
168
|
+
- G1: 1056/1056 ficheiros de codigo com simbolos (100.0%); 0 sem simbolos, dos quais 0 em testes. Lista: nenhum
|
|
169
|
+
- G2: INFERRED 14.05% (12.87% so em fontes main); INFERRED por relacao {'calls': 5980, 'references': 2534}.
|
|
170
|
+
- G8 determinismo: assinatura 703f4318fc6b20a3 vs 703f4318fc6b20a3, igual: True.
|
|
171
|
+
- Incremental (1 ficheiro alterado: agents/cloud-engineer/src/main/java/io/visionhammer/agents/agent1/controller/Agent1Controller.java): 18.68s, 1123.9 MB; simbolo novo no grafo: True; sem alteracoes: 11.32s.
|
|
172
|
+
- G7 rede bloqueada (sandbox-exec (deny network*)): exit 0, 0 eventos socket, 2 subprocess.Popen (('git', ['git', 'rev-parse', 'HEAD'], '/private/tmp/claude-501/-Users-).
|
|
173
|
+
- G2 amostra (seed 20261028, n=60): oraculo ok/wrong/unknown {'ok': 53, 'unknown': 7}; precisao nas decididas 100.0%; estrato ambiguo {'n': 20, 'ok': 18, 'precision_pct': 100.0, 'unknown': 2, 'wrong': 0}; estrato outros {'n': 40, 'ok': 35, 'precision_pct': 100.0, 'unknown': 5, 'wrong': 0}. Revisao manual: 0 linhas.
|
|
174
|
+
|
|
175
|
+
`affected` (profundidade 1) contra `rg -w`, so fontes main:
|
|
176
|
+
|
|
177
|
+
| Simbolo | grafo | rg | ambos | so grafo | so rg | recall vs rg | precisao vs rg |
|
|
178
|
+
|---|---|---|---|---|---|---|---|
|
|
179
|
+
| ContextAssembler (ok) | 7 | 15 | 7 | 0 | 8 | 46.7% | 100.0% |
|
|
180
|
+
| VoyageEmbeddingClient (ok) | 7 | 9 | 7 | 0 | 2 | 77.8% | 100.0% |
|
|
181
|
+
| PiiEgressMasker (ok) | 21 | 21 | 21 | 0 | 0 | 100.0% | 100.0% |
|
|
182
|
+
| SecretMasker (ok) | 1 | 1 | 1 | 0 | 0 | 100.0% | 100.0% |
|
|
183
|
+
| CorrelationIdDetector (ok) | 2 | 2 | 2 | 0 | 0 | 100.0% | 100.0% |
|
|
184
|
+
| LiteLlmChatAdapter (ok) | 11 | 1 | 0 | 11 | 1 | 0.0% | 0.0% |
|
|
185
|
+
| PolicyGateGuard (ok) | 4 | 4 | 4 | 0 | 0 | 100.0% | 100.0% |
|
|
186
|
+
| ChunkRemaskService (ok) | 1 | 3 | 1 | 0 | 2 | 33.3% | 100.0% |
|
|
187
|
+
|
|
188
|
+
Agregado (profundidade 1): recall 76.8%, precisao 79.6% (8 simbolos); profundidade 2: recall 76.8%.
|
|
189
|
+
|
|
190
|
+
recall@8 no conjunto de referencia (8 perguntas):
|
|
191
|
+
|
|
192
|
+
| Backend | Tipo | n | recall@8 | acerto@8 |
|
|
193
|
+
|---|---|---|---|---|
|
|
194
|
+
| graph | all | 8 | 62.5% | 62.5% |
|
|
195
|
+
| graph | conceptual | 7 | 57.1% | 57.1% |
|
|
196
|
+
| graph | symbolic | 1 | 100.0% | 100.0% |
|
|
197
|
+
| graph_call_only | all | 8 | 62.5% | 62.5% |
|
|
198
|
+
| graph_call_only | conceptual | 7 | 57.1% | 57.1% |
|
|
199
|
+
| graph_call_only | symbolic | 1 | 100.0% | 100.0% |
|
|
200
|
+
| lexical_auto | all | 8 | 75.0% | 75.0% |
|
|
201
|
+
| lexical_auto | conceptual | 7 | 71.4% | 71.4% |
|
|
202
|
+
| lexical_auto | symbolic | 1 | 100.0% | 100.0% |
|
|
203
|
+
| lexical_curated | all | 8 | 100.0% | 100.0% |
|
|
204
|
+
| lexical_curated | conceptual | 7 | 100.0% | 100.0% |
|
|
205
|
+
| lexical_curated | symbolic | 1 | 100.0% | 100.0% |
|
|
206
|
+
|
|
207
|
+
`lexical_auto` usa so o texto da pergunta (baseline justo para o fallback ripgrep); `lexical_curated` usa palavras escolhidas por quem conhecia a resposta (teto otimista, nao comparar).
|
|
208
|
+
| vector (pgvector) | - | - | NAO MEDIDO | NAO MEDIDO |
|
|
209
|
+
|
|
210
|
+
## 3. Spring/DI (fixture sintetica)
|
|
211
|
+
|
|
212
|
+
| Grupo | Encontrado | Esperado | % |
|
|
213
|
+
|---|---|---|---|
|
|
214
|
+
| bean_methods | 2 | 4 | 50.0% |
|
|
215
|
+
| interface_impl | 5 | 8 | 62.5% |
|
|
216
|
+
| lombok_injection | 4 | 4 | 100.0% |
|
|
217
|
+
| qualifier | 0 | 3 | 0.0% |
|
|
218
|
+
|
|
219
|
+
Itens em falta:
|
|
220
|
+
|
|
221
|
+
- bean_methods / bean-clock-consumed-by-audit-bean: not reachable within 2 hops
|
|
222
|
+
- bean_methods / bean-consumer-checkout-audit: not reachable within 2 hops
|
|
223
|
+
- interface_impl / checkout-reaches-stripe-impl: not reachable within 2 hops
|
|
224
|
+
- interface_impl / refund-reaches-paypal-impl: not reachable within 2 hops
|
|
225
|
+
- interface_impl / refund-reaches-stripe-impl: not reachable within 2 hops
|
|
226
|
+
- qualifier / qualifier-stripe-selected: not reachable within 2 hops
|
|
227
|
+
- qualifier / qualifier-stripe-not-paypal: vacuous: companion check qualifier-stripe-selected failed, so absence proves nothing
|
|
228
|
+
- qualifier / qualifier-fastcache-bean: not reachable within 2 hops
|
|
229
|
+
|
|
230
|
+
## 4. Licencas das dependencias instaladas
|
|
231
|
+
|
|
232
|
+
32 distribuicoes; permissivas 32; a rever nenhuma; desconhecidas nenhuma; sem ficheiro de licenca ['tree-sitter-groovy'].
|
|
233
|
+
|
|
234
|
+
## 5. pip-audit
|
|
235
|
+
|
|
236
|
+
0 vulnerabilidades conhecidas em 32 pacotes (exit 0)
|
|
237
|
+
|
|
238
|
+
## 6. O que este relatorio nao mediu
|
|
239
|
+
|
|
240
|
+
- recall@8 contra pgvector: precisa do dataset do RagEvalController e da BD de QA (passar `--vector-results`).
|
|
241
|
+
- G6 (incidentes ponta a ponta sem Voyage), G9 (imagem local-agent-full) e G10 (repo do piloto, Python/SQL/Go/Terraform reais).
|
|
242
|
+
- Revisao manual por 2 pessoas das arestas INFERRED (o CSV em `review/` esta pronto para isso).
|
|
243
|
+
- Desempenho em 2 vCPU/4 GB e no hardware do piloto.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Releasing (npm)
|
|
2
|
+
|
|
3
|
+
Every push to `main` publishes `deepdebug-local-agent` to npm through
|
|
4
|
+
`.github/workflows/npm-publish.yml` (Trusted Publishing, OIDC; no token). The version
|
|
5
|
+
is chosen automatically from the commit messages since the last release tag, following
|
|
6
|
+
[Semantic Versioning](https://semver.org) and [Conventional Commits](https://www.conventionalcommits.org):
|
|
7
|
+
|
|
8
|
+
| Level | Version step | Commit that triggers it |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| Patch | `0.0.X` bug fix | `fix:`, and any other type (`perf:`, `refactor:`, `docs:`, `chore:`, `ci:`, ...) or a message without a type |
|
|
11
|
+
| Minor | `0.X.0` new functionality | `feat:` or `feat(scope):` |
|
|
12
|
+
| Major | `X.0.0` big, breaking change | `type!:` (for example `feat!:`) or a `BREAKING CHANGE:` footer |
|
|
13
|
+
|
|
14
|
+
The highest level among the commits since the last `vX.Y.Z` tag wins. Merge commits and the
|
|
15
|
+
CI bump commits (`[skip ci]`) are ignored. The rules live in `scripts/next-version.mjs` and are
|
|
16
|
+
unit tested in `test/next-version.test.js`.
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
fix: reject unsigned requests on /workspace/exec -> 1.1.0 becomes 1.1.1
|
|
22
|
+
feat(ws): metadata-only channel -> 1.1.1 becomes 1.2.0
|
|
23
|
+
feat!: require node 22 -> 1.2.0 becomes 2.0.0
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Tips
|
|
27
|
+
|
|
28
|
+
- Write the PR title as a Conventional Commit and use "Squash and merge", so the version
|
|
29
|
+
follows the title. With a normal merge, the commits inside the PR decide.
|
|
30
|
+
- To skip a release (for example a docs-only change), put `[skip ci]` in the merge commit
|
|
31
|
+
subject.
|
|
32
|
+
- To force a version, run the workflow by hand with the `version` input:
|
|
33
|
+
`gh workflow run npm-publish.yml -f version=X.Y.Z` (must be greater than the highest tag).
|
|
34
|
+
- The workflow tags and commits the bump only after a successful publish, so a failed
|
|
35
|
+
publish does not burn a version. npm reviews each new version automatically: it can stay
|
|
36
|
+
in "Validating" for a few minutes before it becomes installable.
|
|
37
|
+
- Provenance is not published (`--provenance` is rejected by npm for private repositories).
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Signed and allow-listed commands — operator notes
|
|
2
|
+
|
|
3
|
+
The platform (vh-api-gateway, visionhammer-agents) signs every command it sends
|
|
4
|
+
to a local agent with an Ed25519 key, and the agent runs shell commands only
|
|
5
|
+
from an allowlist. Both checks have three modes, `off | warn | enforce`. The
|
|
6
|
+
default is `enforce` (signature: since 1.1.0; exec policy: since 1.0.35), and an
|
|
7
|
+
unrecognised value also means `enforce`. `warn` verifies and logs without
|
|
8
|
+
rejecting; use it only as an explicit, temporary opt-out
|
|
9
|
+
(`LOCAL_AGENT_SIGNATURE_MODE=warn`) for an agent whose platform side does not
|
|
10
|
+
sign yet. Without a listen address the agent also binds `127.0.0.1` unless a
|
|
11
|
+
token (`LOCAL_AGENT_TOKEN` / `AGENT_SHARED_SECRET`) or Cloud Run is present.
|
|
12
|
+
|
|
13
|
+
## Protocol v1
|
|
14
|
+
|
|
15
|
+
HTTP headers on every platform → agent request:
|
|
16
|
+
|
|
17
|
+
| Header | Value |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `X-VH-Key-Id` | id of the signing key, for example `vh-2026-09` |
|
|
20
|
+
| `X-VH-Timestamp` | unix epoch **seconds** |
|
|
21
|
+
| `X-VH-Nonce` | 16–128 chars `[A-Za-z0-9_-]`, unique per request |
|
|
22
|
+
| `X-VH-Tenant-Id` / `X-VH-Workspace-Id` | the tenant and workspace the platform acts for (`""` when unknown) |
|
|
23
|
+
| `X-VH-Signature` | base64 Ed25519 signature over the canonical string |
|
|
24
|
+
|
|
25
|
+
Canonical string (UTF-8, `\n`-joined, no trailing newline):
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
VH-AGENT-SIG-V1
|
|
29
|
+
<METHOD upper-case>
|
|
30
|
+
<raw path + "?" + raw query, as sent>
|
|
31
|
+
<hex sha256 of the exact body bytes (sha256("") when empty)>
|
|
32
|
+
<tenantId>
|
|
33
|
+
<workspaceId>
|
|
34
|
+
<timestamp>
|
|
35
|
+
<nonce>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Golden vector (all three implementations test against it): method `post`, path
|
|
39
|
+
`/workspace/exec?x=1`, body `{}`, tenant `t`, workspace `w`, timestamp
|
|
40
|
+
`1790000000`, nonce `abcdefghijklmnop` →
|
|
41
|
+
`VH-AGENT-SIG-V1\nPOST\n/workspace/exec?x=1\n44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a\nt\nw\n1790000000\nabcdefghijklmnop`.
|
|
42
|
+
|
|
43
|
+
WebSocket tunnel: the envelope gains `paramsRaw` (the exact JSON the platform
|
|
44
|
+
signed) and `sig: {keyId, timestamp, nonce, tenantId, workspaceId, signature}`.
|
|
45
|
+
Method is `WS`, path is the command name, body is `paramsRaw`. A verified
|
|
46
|
+
envelope runs `JSON.parse(paramsRaw)`; the unsigned `params` copy is used only
|
|
47
|
+
by older agents, or in `warn`/`off` mode when the envelope is not signed.
|
|
48
|
+
|
|
49
|
+
The agent rejects (in `enforce`) with 401 when:
|
|
50
|
+
|
|
51
|
+
* the request has no signature, or the key id is unknown;
|
|
52
|
+
* the signature does not verify;
|
|
53
|
+
* `|now − timestamp| > 60 s`;
|
|
54
|
+
* the nonce was already used (in-memory LRU, TTL 120 s, 100k entries);
|
|
55
|
+
* the tenant does not match the agent's identity (`LOCAL_AGENT_TENANT_ID` →
|
|
56
|
+
`TENANT_ID` → `~/.deepdebug/config.json`). If the signed workspace is
|
|
57
|
+
non-empty, it must match `LOCAL_AGENT_WORKSPACE_ID` / `WORKSPACE_ID` (set
|
|
58
|
+
`LOCAL_AGENT_REQUIRE_WORKSPACE=true` to require it on every call). A shared
|
|
59
|
+
operator agent with no identity accepts any signed tenant.
|
|
60
|
+
|
|
61
|
+
`X-Agent-Token` (`LOCAL_AGENT_TOKEN`) is still checked first, as the second
|
|
62
|
+
factor. `GET /health` and `/metrics` stay public.
|
|
63
|
+
|
|
64
|
+
## Exec allowlist
|
|
65
|
+
|
|
66
|
+
`/workspace/exec`, `/workspace/run`, `/mcp/execute-command` and
|
|
67
|
+
`/setup/run-command` accept a command only when every segment (`&&`, `||`,
|
|
68
|
+
`;`, `|`) belongs to one of these families: read-only git; package-manager
|
|
69
|
+
install, test, build or lint for an ecosystem detected in the workspace (npm,
|
|
70
|
+
yarn, pnpm, mvn/mvnw, gradle/gradlew, pip, poetry, uv, go, cargo, dotnet); test
|
|
71
|
+
runners; linters; file read/write inside the workspace root; or `cd` inside
|
|
72
|
+
the root. `$VAR`, `$(...)`, backticks, subshells, `&`, here-docs, process
|
|
73
|
+
substitution and paths outside the root are refused.
|
|
74
|
+
|
|
75
|
+
Hardening rules:
|
|
76
|
+
|
|
77
|
+
* **Wrappers are validated recursively.** `env`, `time`, `nice`, `timeout`,
|
|
78
|
+
`c8`, `nyc`, `npx`, `npm/pnpm exec`, `poetry run` and `uv run` wrap another
|
|
79
|
+
command, and that command must pass the same policy. `xargs` is refused.
|
|
80
|
+
Interpreter eval (`node -e/-p`, `python -c`, `sh/bash -c`) is never
|
|
81
|
+
allowed. Test runners refuse inline JSON config.
|
|
82
|
+
* **Paths are realpath-resolved.** Every non-flag token that is path-like or
|
|
83
|
+
exists on disk, every `--opt=value`, every redirect target and every
|
|
84
|
+
`cd`/`git -C` directory is resolved with symlinks followed and must stay
|
|
85
|
+
inside the root. An unquoted glob may use wildcards only in its last segment,
|
|
86
|
+
and none of the symlinks it can match may point outside.
|
|
87
|
+
* **Protected files.** A redirect, `tee`, `cp`, `mv`, `rm`, `touch`, `chmod`
|
|
88
|
+
or `sort -o` is refused on these paths: `.git/**`, hook dirs (`.husky`,
|
|
89
|
+
`.githooks`), and build/test config that executes code. That includes
|
|
90
|
+
`package.json`, `.npmrc`, lockfiles, `Makefile`, `pom.xml`, `*.gradle*`,
|
|
91
|
+
`gradle/wrapper`, `setup.py`, `pyproject.toml`, `conftest.py`, and
|
|
92
|
+
`*.config.js` for jest, vitest, eslint and similar tools. Those files change
|
|
93
|
+
only through the signed apply-patch tool.
|
|
94
|
+
* **No write-then-execute.** A command string that writes files may not run a
|
|
95
|
+
package-manager, test or lint tool in a later segment. The source of truth is
|
|
96
|
+
`src/utils/exec-policy.js`; the reason is returned in the 403 body.
|
|
97
|
+
|
|
98
|
+
Two exceptions:
|
|
99
|
+
|
|
100
|
+
1. **Human approval (REQUIRE_HUMAN).** An `X-VH-Approval` header (or
|
|
101
|
+
`approvalToken` in the body) holding a PolicyGate token
|
|
102
|
+
`vhapr1.<keyId>.<b64url payload>.<b64url sig>`. It is signed with the same
|
|
103
|
+
platform key over `VH-HUMAN-APPROVAL-V1\n<keyId>\n<payload segment>`. The
|
|
104
|
+
token is bound to the SHA-256 of the exact command, the tenant and the
|
|
105
|
+
workspace. It lives at most 15 minutes and can be used once.
|
|
106
|
+
2. **Platform-authored tools.** A body with `commandOrigin: "platform"` and a
|
|
107
|
+
known `tool` (`create_branch`, `git_commit`, `git_push`, `close_pr`,
|
|
108
|
+
`delete_branch`, `publish_scope`, `current_branch`). It is honoured only
|
|
109
|
+
when the request signature verified. The model's free-form `run_command`
|
|
110
|
+
never carries it.
|
|
111
|
+
|
|
112
|
+
## Rollout
|
|
113
|
+
|
|
114
|
+
1. **Generate the keypair** on a trusted workstation. Keep the private key
|
|
115
|
+
off disk after upload.
|
|
116
|
+
```sh
|
|
117
|
+
openssl genpkey -algorithm ed25519 -out local-agent-signing-key.pem
|
|
118
|
+
openssl pkey -in local-agent-signing-key.pem -pubout -outform DER | base64 # SPKI, 60 chars
|
|
119
|
+
```
|
|
120
|
+
2. **Store the private key** in Secret Manager as `local-agent-signing-key`
|
|
121
|
+
(PKCS#8 PEM) in `visionhammer`, and grant `secretAccessor` to the gateway
|
|
122
|
+
and agent2 runtime service accounts.
|
|
123
|
+
```sh
|
|
124
|
+
gcloud secrets create local-agent-signing-key --replication-policy=automatic --data-file=local-agent-signing-key.pem
|
|
125
|
+
```
|
|
126
|
+
3. **Mount it on the signers.** On gateway and software-engineer (agent2)
|
|
127
|
+
Cloud Run, set
|
|
128
|
+
`--set-secrets=LOCAL_AGENT_SIGNING_KEY=local-agent-signing-key:latest` and
|
|
129
|
+
`--update-env-vars=LOCAL_AGENT_SIGNING_KEY_ID=vh-2026-09`. Without the key
|
|
130
|
+
they log once and send unsigned requests, as before. Remember
|
|
131
|
+
`--vpc-egress=private-ranges-only` on `run services update`.
|
|
132
|
+
4. **Give the agents the public key:**
|
|
133
|
+
* Image default: add `{"keyId":"vh-2026-09","publicKey":"<SPKI b64>"}` to
|
|
134
|
+
`src/config/trusted-signing-keys.json` and cut a new image.
|
|
135
|
+
* BYOC provisioning: set `LOCAL_AGENT_SIGNING_PUBLIC_KEYS=vh-2026-09=<SPKI b64>`
|
|
136
|
+
on the gateway. `CloudEngineerService` copies it, along with
|
|
137
|
+
`LOCAL_AGENT_SIGNATURE_MODE` and `LOCAL_AGENT_EXEC_POLICY_MODE`, into
|
|
138
|
+
every ACI/Cloud Run agent it provisions.
|
|
139
|
+
* Operator QA agent: set the same env on `deepdebug-local-agent-qa`.
|
|
140
|
+
5. **Soak in `warn`.** Watch the logs for `[agent-sig] WARN` and
|
|
141
|
+
`[exec-policy] WARN`. The logs are rate-limited, one line per reason and
|
|
142
|
+
path per minute. Expect zero signature warnings from updated platform
|
|
143
|
+
callers. Every exec warning is a command that `enforce` would block.
|
|
144
|
+
6. **Flip to enforce** per agent: `LOCAL_AGENT_SIGNATURE_MODE=enforce` once
|
|
145
|
+
the signature warnings are zero. Set the gateway env to `enforce` so new
|
|
146
|
+
BYOC agents start enforced. Roll back by setting `warn` again, with no
|
|
147
|
+
redeploy of the platform.
|
|
148
|
+
|
|
149
|
+
Since 1.0.35 (security review 2026-10-11) the **exec policy defaults to
|
|
150
|
+
`enforce`**: a command outside the allowlist is refused unless it carries a
|
|
151
|
+
PolicyGate approval token or is a signed platform tool command. Set
|
|
152
|
+
`LOCAL_AGENT_EXEC_POLICY_MODE=warn` explicitly on an agent that still needs
|
|
153
|
+
the soak. In the same release the HTTP perimeter stopped defaulting to
|
|
154
|
+
`compat`: `AGENT_SHARED_SECRET` is enforced as `X-Agent-Token`, and an agent
|
|
155
|
+
with no token outside Cloud Run only answers loopback callers (the reverse
|
|
156
|
+
WS tunnel). `LOCAL_AGENT_AUTH=compat` is the explicit, logged opt-out.
|
|
157
|
+
|
|
158
|
+
### Key rotation
|
|
159
|
+
|
|
160
|
+
Add the new public key next to the old one (`kid1=...,kid2=...` / two file
|
|
161
|
+
entries) on every agent. Then switch `LOCAL_AGENT_SIGNING_KEY` and
|
|
162
|
+
`LOCAL_AGENT_SIGNING_KEY_ID` on the signers to the new version. After a day,
|
|
163
|
+
remove the old public key. Approval tokens use the same keys.
|
package/env.example
CHANGED
|
@@ -39,3 +39,37 @@ LOG_LEVEL=info
|
|
|
39
39
|
|
|
40
40
|
# For on-premise DeepDebug server
|
|
41
41
|
# DEEPDEBUG_API_URL=https://deepdebug.internal.company.com
|
|
42
|
+
|
|
43
|
+
# ─── Security (audit 2026-09) ───
|
|
44
|
+
# LOCAL_AGENT_TOKEN: when set, every route except GET /health and /metrics
|
|
45
|
+
# requires the X-Agent-Token header. Set it only once every caller (gateway,
|
|
46
|
+
# agents) sends the header. AGENT_SHARED_SECRET (injected by the gateway on
|
|
47
|
+
# BYOC ACI agents) is enforced the same way when LOCAL_AGENT_TOKEN is not set.
|
|
48
|
+
# With neither (and outside Cloud Run) the agent is loopback-only: it binds
|
|
49
|
+
# 127.0.0.1 and refuses non-loopback callers. LOCAL_AGENT_BIND_HOST overrides.
|
|
50
|
+
# LOCAL_AGENT_TOKEN=
|
|
51
|
+
# Extra browser origins allowed by CORS (comma-separated, exact match).
|
|
52
|
+
# LOCAL_AGENT_CORS_ORIGINS=
|
|
53
|
+
# Re-enable /setup/run-command, /setup/install-tool, /setup/run-auth-command.
|
|
54
|
+
# LOCAL_AGENT_ENABLE_SETUP_COMMANDS=false
|
|
55
|
+
# LOCAL_AGENT_AUTH=loopback -> accept loopback callers only (laptop + WS tunnel)
|
|
56
|
+
# LOCAL_AGENT_AUTH=off -> emergency opt-out (NOT recommended)
|
|
57
|
+
|
|
58
|
+
# ─── Signed + allow-listed commands (docs/signed-commands.md) ───
|
|
59
|
+
# Trusted platform public keys (Ed25519), keyId=base64 (raw 32-byte or SPKI DER).
|
|
60
|
+
# Several entries = rotation window. The image default lives in
|
|
61
|
+
# src/config/trusted-signing-keys.json.
|
|
62
|
+
# LOCAL_AGENT_SIGNING_PUBLIC_KEYS=vh-2026-09=MCowBQYDK2VwAyEA...
|
|
63
|
+
# off | warn | enforce (default) — verify X-VH-Signature on every command.
|
|
64
|
+
# Set warn only as a loud, temporary opt-out for a platform that does not sign yet.
|
|
65
|
+
# LOCAL_AGENT_SIGNATURE_MODE=enforce
|
|
66
|
+
# off | warn | enforce (default) — shell allowlist on exec endpoints.
|
|
67
|
+
# LOCAL_AGENT_EXEC_POLICY_MODE=enforce
|
|
68
|
+
# Identity the signature must match (defaults: TENANT_ID / WORKSPACE_ID).
|
|
69
|
+
# LOCAL_AGENT_TENANT_ID=
|
|
70
|
+
# LOCAL_AGENT_WORKSPACE_ID=
|
|
71
|
+
# LOCAL_AGENT_REQUIRE_WORKSPACE=false
|
|
72
|
+
|
|
73
|
+
# DLP (T4-09): mask secrets/PII before content leaves the agent. Default off.
|
|
74
|
+
# LOCAL_AGENT_DLP_MODE=off|warn|enforce (warn = count only, enforce = redact)
|
|
75
|
+
# LOCAL_AGENT_DLP_BLOCK_CLASSES=private-key,cloud-credential (enforce: withhold whole text)
|
package/helm/Chart.yaml
CHANGED
|
@@ -38,14 +38,24 @@ spec:
|
|
|
38
38
|
value: "production"
|
|
39
39
|
- name: PORT
|
|
40
40
|
value: "5055"
|
|
41
|
-
|
|
41
|
+
# Names aligned to what src/server.js actually reads (server.js frozen):
|
|
42
|
+
# GATEWAY_URL (server.js:73), TENANT_ID (:7505), WORKSPACE_ROOT (:637).
|
|
43
|
+
- name: GATEWAY_URL
|
|
42
44
|
value: {{ .Values.config.apiUrl | quote }}
|
|
43
|
-
- name:
|
|
45
|
+
- name: TENANT_ID
|
|
44
46
|
valueFrom:
|
|
45
47
|
secretKeyRef:
|
|
46
48
|
name: {{ include "deepdebug-agent.fullname" . }}-secret
|
|
47
49
|
key: tenant-id
|
|
48
|
-
-
|
|
50
|
+
# Without a token the agent is loopback-only (bind 127.0.0.1) and the
|
|
51
|
+
# kubelet probes would fail; with it, every route but /health and
|
|
52
|
+
# /metrics needs X-Agent-Token.
|
|
53
|
+
- name: LOCAL_AGENT_TOKEN
|
|
54
|
+
valueFrom:
|
|
55
|
+
secretKeyRef:
|
|
56
|
+
name: {{ include "deepdebug-agent.fullname" . }}-secret
|
|
57
|
+
key: agent-token
|
|
58
|
+
- name: WORKSPACE_ROOT
|
|
49
59
|
value: {{ .Values.config.workspacePath | quote }}
|
|
50
60
|
- name: LOG_LEVEL
|
|
51
61
|
value: {{ .Values.config.logLevel | quote }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{{- if .Values.networkPolicy.enabled }}
|
|
2
|
+
apiVersion: networking.k8s.io/v1
|
|
3
|
+
kind: NetworkPolicy
|
|
4
|
+
metadata:
|
|
5
|
+
name: {{ include "deepdebug-agent.fullname" . }}
|
|
6
|
+
labels:
|
|
7
|
+
{{- include "deepdebug-agent.labels" . | nindent 4 }}
|
|
8
|
+
spec:
|
|
9
|
+
podSelector:
|
|
10
|
+
matchLabels:
|
|
11
|
+
{{- include "deepdebug-agent.selectorLabels" . | nindent 6 }}
|
|
12
|
+
policyTypes:
|
|
13
|
+
- Ingress
|
|
14
|
+
{{- with .Values.networkPolicy.allowFrom }}
|
|
15
|
+
ingress:
|
|
16
|
+
- from:
|
|
17
|
+
{{- toYaml . | nindent 8 }}
|
|
18
|
+
ports:
|
|
19
|
+
- protocol: TCP
|
|
20
|
+
port: 5055
|
|
21
|
+
{{- end }}
|
|
22
|
+
{{- end }}
|
package/helm/values.yaml
CHANGED
|
@@ -27,6 +27,9 @@ replicaCount: 1
|
|
|
27
27
|
# ─────────────────────────────────────────
|
|
28
28
|
config:
|
|
29
29
|
tenantId: "" # Required: Your tenant ID
|
|
30
|
+
# Required: shared secret for X-Agent-Token (LOCAL_AGENT_TOKEN). Pass it with
|
|
31
|
+
# --set config.agentToken=$(openssl rand -hex 32); never commit it.
|
|
32
|
+
agentToken: ""
|
|
30
33
|
apiUrl: "https://api.deepdebug.ai"
|
|
31
34
|
logLevel: "info"
|
|
32
35
|
|
|
@@ -117,20 +120,6 @@ readinessProbe:
|
|
|
117
120
|
timeoutSeconds: 5
|
|
118
121
|
failureThreshold: 3
|
|
119
122
|
|
|
120
|
-
# ─────────────────────────────────────────
|
|
121
|
-
# Network Policies (Enterprise)
|
|
122
|
-
# ─────────────────────────────────────────
|
|
123
|
-
networkPolicy:
|
|
124
|
-
enabled: true
|
|
125
|
-
# Allow egress only to DeepDebug API
|
|
126
|
-
egressRules:
|
|
127
|
-
- to:
|
|
128
|
-
- ipBlock:
|
|
129
|
-
cidr: 0.0.0.0/0
|
|
130
|
-
ports:
|
|
131
|
-
- protocol: TCP
|
|
132
|
-
port: 443
|
|
133
|
-
|
|
134
123
|
# ─────────────────────────────────────────
|
|
135
124
|
# Pod Disruption Budget
|
|
136
125
|
# ─────────────────────────────────────────
|
|
@@ -160,3 +149,13 @@ podAnnotations:
|
|
|
160
149
|
prometheus.io/scrape: "true"
|
|
161
150
|
prometheus.io/port: "5055"
|
|
162
151
|
prometheus.io/path: "/metrics"
|
|
152
|
+
|
|
153
|
+
# ─────────────────────────────────────────
|
|
154
|
+
# Network Policy (security review 2026-10-11, C1)
|
|
155
|
+
# ─────────────────────────────────────────
|
|
156
|
+
# The agent reaches the platform over an OUTBOUND tunnel, so it needs no
|
|
157
|
+
# inbound traffic except kubelet probes. Default: deny all ingress except the
|
|
158
|
+
# sources listed in allowFrom (NetworkPolicy peers, e.g. a podSelector).
|
|
159
|
+
networkPolicy:
|
|
160
|
+
enabled: true
|
|
161
|
+
allowFrom: []
|
package/mcp-server.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ListToolsRequestSchema,
|
|
7
7
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
8
|
import path from "path";
|
|
9
|
+
import { createAuthMiddleware } from "./src/utils/agent-auth.js";
|
|
9
10
|
import { spawn } from "child_process";
|
|
10
11
|
import { exists, listRecursive, readFile, writeFile, stat } from "./fs-utils.js";
|
|
11
12
|
import { detectProject, readText } from "./detectors.js";
|
|
@@ -454,6 +455,8 @@ function errorResponse(message) {
|
|
|
454
455
|
export async function startMCPHttpServer(wsManager, port = 5056) {
|
|
455
456
|
const express = (await import("express")).default;
|
|
456
457
|
const app = express();
|
|
458
|
+
// SECURITY: execute_command runs `sh -c`; same auth perimeter as the main API.
|
|
459
|
+
app.use(createAuthMiddleware());
|
|
457
460
|
|
|
458
461
|
const server = createMCPServer(wsManager);
|
|
459
462
|
|
|
@@ -522,7 +525,7 @@ export async function startMCPHttpServer(wsManager, port = 5056) {
|
|
|
522
525
|
}
|
|
523
526
|
});
|
|
524
527
|
|
|
525
|
-
app.listen(port, () => {
|
|
528
|
+
app.listen(port, process.env.MCP_BIND_HOST || "127.0.0.1", () => {
|
|
526
529
|
console.log(`🧠 DeepDebug MCP Server listening on http://localhost:${port}`);
|
|
527
530
|
console.log(` SSE: http://localhost:${port}/sse`);
|
|
528
531
|
console.log(` REST: http://localhost:${port}/tool/{toolName}`);
|