@khoavu882/doflow 2.0.0-beta.5 → 2.0.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/README.md +12 -11
- package/bin/doflow.js +21 -11
- package/core/.antigravity-plugin/plugin.json +1 -2
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/lib.sh +43 -24
- package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
- package/core/harnesses/claude/hooks/post-compact.sh +35 -32
- package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
- package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
- package/core/harnesses/claude/hooks/session-end.sh +23 -10
- package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
- package/core/harnesses/claude/settings/settings.json +18 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
- package/core/harnesses/codex/agents/core-implementer.toml +1 -0
- package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
- package/core/harnesses/codex/agents/research-writer.toml +1 -1
- package/core/harnesses/codex/agents/reviewer.toml +10 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
- package/core/harnesses/codex/agents/system-architect.toml +1 -1
- package/core/harnesses/codex/hooks/lib.sh +44 -25
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
- package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
- package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
- package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
- package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
- package/core/harnesses/copilot/hooks/doflow.json +57 -0
- package/core/harnesses/kiro/agents/doflow.json +1 -1
- package/core/harnesses/kiro/hooks/lib.sh +26 -6
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
- package/core/harnesses/kiro/hooks/session-start.sh +1 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
- package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
- package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
- package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
- package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
- package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
- package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
- package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
- package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
- package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
- package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
- package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
- package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
- package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
- package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
- package/core/harnesses/shared/locator/doflow-run +2 -3
- package/core/registry/advice.json +3 -3
- package/core/registry/assets.json +55 -82
- package/core/registry/capabilities.json +12 -0
- package/core/registry/contracts.json +58 -31
- package/core/registry/contribution-points.json +32 -0
- package/core/registry/gates.json +32 -0
- package/core/registry/guidance-caps.json +40 -0
- package/core/registry/harnesses.json +1442 -363
- package/core/registry/lifecycle.json +485 -304
- package/core/registry/mcp.json +5 -1
- package/core/registry/models.json +7 -1
- package/core/registry/package-templates/README.md +19 -0
- package/core/registry/package-templates/agent/files/agent.md +9 -0
- package/core/registry/package-templates/agent/template.json +4 -0
- package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
- package/core/registry/package-templates/capability-provider/template.json +11 -0
- package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
- package/core/registry/package-templates/debate-preset/template.json +4 -0
- package/core/registry/package-templates/review-preset/files/preset.json +30 -0
- package/core/registry/package-templates/review-preset/template.json +4 -0
- package/core/registry/package-templates/skill/files/SKILL.md +8 -0
- package/core/registry/package-templates/skill/template.json +4 -0
- package/core/registry/package-templates/worker/files/worker.json +92 -0
- package/core/registry/package-templates/worker/template.json +11 -0
- package/core/registry/packages/doflow/doflow-package.json +15 -0
- package/core/registry/packages/doflow/presets/debate-3.json +26 -0
- package/core/registry/protocols.json +61 -0
- package/core/registry/routes.json +17 -0
- package/core/registry/rules.json +431 -0
- package/core/registry/settings.json +276 -0
- package/core/registry/storage.json +679 -31
- package/core/registry/telemetry.json +236 -0
- package/core/registry/transcripts.json +142 -0
- package/core/registry/verification.json +76 -1
- package/core/registry/workflows.json +55 -0
- package/core/shared/agent-specs/core-implementer.md +3 -0
- package/core/shared/agent-specs/delivery-lead.md +68 -0
- package/core/shared/agent-specs/quality-guardian.md +5 -1
- package/core/shared/agent-specs/research-writer.md +10 -3
- package/core/shared/agent-specs/reviewer.md +53 -0
- package/core/shared/agent-specs/spec-analyst.md +5 -6
- package/core/shared/agent-specs/system-architect.md +5 -1
- package/core/shared/guidance/DOFLOW_CORE.md +1 -5
- package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
- package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
- package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
- package/core/shared/guidance/pointers/codex.md +1 -4
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
- package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
- package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
- package/core/shared/guidance/rules/RULES.md +45 -0
- package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
- package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
- package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
- package/core/shared/scripts/doflow/bin/doflow-run +149 -34
- package/core/shared/skills/do/SKILL.md +12 -10
- package/core/shared/skills/do/references/advice_calls.md +26 -0
- package/core/shared/skills/do/references/pm_routing.md +1 -2
- package/core/shared/skills/do/references/program_mode.md +31 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
- package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
- package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
- package/core/shared/skills/do-code-review/SKILL.md +41 -144
- package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
- package/core/shared/skills/do-code-review/references/tools.md +90 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
- package/core/shared/skills/do-design/SKILL.md +18 -17
- package/core/shared/skills/do-diagnose/SKILL.md +14 -4
- package/core/shared/skills/do-document/SKILL.md +11 -1
- package/core/shared/skills/do-document/references/deep_research.md +1 -2
- package/core/shared/skills/do-document/references/embedded-research.md +18 -6
- package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
- package/core/shared/skills/do-execute-plan/SKILL.md +15 -58
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
- package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
- package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
- package/core/shared/skills/do-flow/SKILL.md +8 -9
- package/core/shared/skills/do-flow/references/task_classes.md +2 -2
- package/core/shared/skills/do-git/SKILL.md +4 -0
- package/core/shared/skills/do-implement/SKILL.md +4 -7
- package/core/shared/skills/do-plan/SKILL.md +26 -15
- package/core/shared/skills/do-test/SKILL.md +7 -4
- package/core/shared/templates/doflow/constitution-template.md +9 -3
- package/core/shared/templates/doflow/data-model-template.md +1 -1
- package/core/shared/templates/doflow/design-template.md +113 -16
- package/core/shared/templates/doflow/plan-template.md +11 -10
- package/core/shared/templates/doflow/question-log-template.md +14 -2
- package/core/shared/templates/doflow/requirement-template.md +1 -1
- package/core/worker-evidence/antigravity/checks.json +14 -0
- package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
- package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
- package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
- package/core/worker-evidence/codex/checks.json +38 -0
- package/package.json +10 -6
- package/src/adapters/antigravity/index.js +143 -66
- package/src/adapters/claude/index.js +24 -4
- package/src/adapters/codex/agents.js +25 -15
- package/src/adapters/codex/index.js +48 -13
- package/src/adapters/codex/native-projection.js +6 -2
- package/src/adapters/copilot/index.js +58 -23
- package/src/adapters/copy-tree.js +290 -44
- package/src/adapters/guidance-manifest.js +71 -0
- package/src/adapters/guidance-pointer.js +19 -0
- package/src/adapters/hook-commands.js +6 -10
- package/src/adapters/index.js +1 -0
- package/src/adapters/kiro/index.js +23 -12
- package/src/adapters/opencode/index.js +8 -8
- package/src/adapters/pi/hooks.js +2 -1
- package/src/adapters/pi/index.js +14 -7
- package/src/adapters/resolve-source.js +26 -2
- package/src/cli/commands/install.js +2 -1
- package/src/cli/commands/reconcile.js +53 -9
- package/src/cli/commands/remove.js +2 -2
- package/src/cli/commands/rollback.js +6 -3
- package/src/cli/commands/self-update.js +18 -9
- package/src/cli/commands/status.js +21 -2
- package/src/cli/commands/ui.js +99 -0
- package/src/cli/commands/update.js +2 -1
- package/src/cli/index.js +322 -29
- package/src/cli/runtime-commands.js +209 -57
- package/src/cli/shared.js +0 -2
- package/src/helper/git.js +1 -1
- package/src/helper/guardrails.js +1 -1
- package/src/helper/marker-merge.js +31 -13
- package/src/helper/package-ids.js +37 -0
- package/src/helper/semver.js +94 -0
- package/src/install/backup.js +105 -15
- package/src/install/context.js +1 -1
- package/src/install/targets.js +31 -16
- package/src/lifecycle/coverage.js +20 -3
- package/src/lifecycle/hook-tier.js +160 -0
- package/src/lifecycle/index.js +412 -78
- package/src/lifecycle/retired-harnesses.js +318 -0
- package/src/lifecycle/view.js +35 -15
- package/src/registry/index.js +551 -26
- package/src/registry/worker-ids.js +45 -0
- package/src/release/build-package.js +0 -3
- package/src/release/docs-blocks.js +492 -0
- package/src/release/preflight.js +425 -0
- package/src/release/rehearse.js +401 -0
- package/src/release/release-workflow.js +733 -0
- package/src/release/sync-plugin-versions.js +43 -7
- package/src/runtime/advice/bench-gate.js +25 -0
- package/src/runtime/advice/cli.js +59 -16
- package/src/runtime/advice/client.js +32 -9
- package/src/runtime/advice/config.js +98 -133
- package/src/runtime/advice/doctor.js +1 -1
- package/src/runtime/advice/elevation.js +97 -0
- package/src/runtime/advice/endpoint-guard.js +9 -3
- package/src/runtime/advice/mode.js +37 -0
- package/src/runtime/advice/record.js +18 -5
- package/src/runtime/advice/registry.js +4 -1
- package/src/runtime/advice/shadow-config.js +229 -0
- package/src/runtime/advice/shadow-dispatcher.js +276 -0
- package/src/runtime/brief-rules.js +81 -0
- package/src/runtime/brief-tools.js +111 -34
- package/src/runtime/candidate.js +20 -11
- package/src/runtime/capabilities/executor.js +450 -0
- package/src/runtime/capabilities/loopback.js +41 -0
- package/src/runtime/capabilities/sanitizer.js +88 -0
- package/src/runtime/capability-router.js +135 -3
- package/src/runtime/checkouts.js +73 -11
- package/src/runtime/claims.js +13 -7
- package/src/runtime/cli.js +196 -19
- package/src/runtime/config-home.js +42 -0
- package/src/runtime/context/arbitrate.js +767 -0
- package/src/runtime/context/delivery-store.js +4 -6
- package/src/runtime/context/emission-rules.js +118 -0
- package/src/runtime/context/identity.js +227 -0
- package/src/runtime/context/primary-command.js +117 -0
- package/src/runtime/context/primary.js +344 -0
- package/src/runtime/context/summary-store.js +655 -0
- package/src/runtime/context-pack.js +154 -78
- package/src/runtime/decision-register.js +13 -4
- package/src/runtime/dispatch/admission.js +392 -0
- package/src/runtime/dispatch/attempt-end.js +274 -0
- package/src/runtime/dispatch/command.js +628 -0
- package/src/runtime/dispatch/controls.js +904 -0
- package/src/runtime/dispatch/envelope.js +276 -0
- package/src/runtime/dispatch/grants.js +310 -0
- package/src/runtime/dispatch/integrate.js +617 -0
- package/src/runtime/dispatch/loop.js +1835 -0
- package/src/runtime/dispatch/store.js +352 -0
- package/src/runtime/dispatch/verify-run.js +201 -0
- package/src/runtime/evidence-ledger.js +34 -7
- package/src/runtime/failure/capture.js +3 -1
- package/src/runtime/failure/cli.js +25 -9
- package/src/runtime/failure/home.js +57 -30
- package/src/runtime/failure/store.js +7 -2
- package/src/runtime/feature-pointer.js +174 -0
- package/src/runtime/feature-resolve.js +13 -2
- package/src/runtime/fleet/command.js +77 -0
- package/src/runtime/fleet/projects.js +178 -0
- package/src/runtime/fleet/tree.js +383 -0
- package/src/runtime/freshness.js +33 -3
- package/src/runtime/git-memo.js +46 -0
- package/src/runtime/guidance/ablation.js +427 -0
- package/src/runtime/health.js +565 -17
- package/src/runtime/history-compactor.js +13 -3
- package/src/runtime/implementation-gate.js +277 -22
- package/src/runtime/inventory/coverage.js +1 -1
- package/src/runtime/inventory/index.js +1 -1
- package/src/runtime/inventory/precedence.js +2 -2
- package/src/runtime/inventory/siblings.js +6 -4
- package/src/runtime/knowledge/code-corpus.js +277 -0
- package/src/runtime/knowledge/code-lexical.js +130 -0
- package/src/runtime/knowledge/code-retrieval.js +307 -0
- package/src/runtime/knowledge/code-validation.js +106 -0
- package/src/runtime/knowledge/gp3-gate.js +74 -0
- package/src/runtime/knowledge/graph-adapter.js +288 -0
- package/src/runtime/knowledge/graph-fallback.js +394 -0
- package/src/runtime/knowledge/hybrid.js +136 -8
- package/src/runtime/knowledge/retrieval.js +34 -1
- package/src/runtime/knowledge/rtk-compress.js +77 -0
- package/src/runtime/knowledge/semble-bridge.py +106 -0
- package/src/runtime/knowledge/semble-lifecycle.js +114 -0
- package/src/runtime/knowledge/semble-provider.js +365 -0
- package/src/runtime/lifecycle/cli.js +16 -6
- package/src/runtime/lifecycle/event-store.js +8 -6
- package/src/runtime/lifecycle/followup.js +6 -1
- package/src/runtime/lifecycle/overview.js +24 -1
- package/src/runtime/lifecycle/report-store.js +8 -10
- package/src/runtime/lifecycle/retention.js +95 -31
- package/src/runtime/lifecycle/root.js +9 -1
- package/src/runtime/lifecycle/status.js +34 -17
- package/src/runtime/lifecycle/store-upkeep.js +56 -7
- package/src/runtime/mask.js +134 -7
- package/src/runtime/mcp-consultations.js +40 -6
- package/src/runtime/model-router.js +79 -7
- package/src/runtime/models/catalogue.js +191 -0
- package/src/runtime/models/command.js +153 -0
- package/src/runtime/off-words.js +12 -0
- package/src/runtime/outcome.js +2 -2
- package/src/runtime/packages/command.js +1009 -0
- package/src/runtime/packages/contributions.js +116 -0
- package/src/runtime/packages/digest.js +132 -0
- package/src/runtime/packages/fetch.js +182 -0
- package/src/runtime/packages/grants.js +192 -0
- package/src/runtime/packages/index-file.js +134 -0
- package/src/runtime/packages/instruction.js +90 -0
- package/src/runtime/packages/manifest.js +331 -0
- package/src/runtime/packages/points.js +477 -0
- package/src/runtime/packages/programs.js +216 -0
- package/src/runtime/packages/scaffold.js +277 -0
- package/src/runtime/packages/source.js +90 -0
- package/src/runtime/packages/state.js +164 -0
- package/src/runtime/packages/store.js +449 -0
- package/src/runtime/packages/tar.js +106 -0
- package/src/runtime/packages/view.js +406 -0
- package/src/runtime/plan-check.js +59 -0
- package/src/runtime/plan-content.js +324 -0
- package/src/runtime/policy-init.js +118 -0
- package/src/runtime/profiles/doctor.js +96 -0
- package/src/runtime/profiles/resolve.js +244 -0
- package/src/runtime/profiles/rollback.js +254 -0
- package/src/runtime/profiles/sampler.js +41 -0
- package/src/runtime/readiness-record.js +16 -11
- package/src/runtime/readiness.js +46 -5
- package/src/runtime/research-request.js +50 -25
- package/src/runtime/retrieval-plan.js +10 -30
- package/src/runtime/retrieval-record.js +75 -5
- package/src/runtime/review/command.js +246 -0
- package/src/runtime/review/dominance.js +68 -0
- package/src/runtime/review/findings.js +434 -0
- package/src/runtime/review/identity.js +255 -0
- package/src/runtime/review/protocol-command.js +109 -0
- package/src/runtime/review/protocols.js +405 -0
- package/src/runtime/review/resolve-protocol.js +302 -0
- package/src/runtime/review/start.js +530 -0
- package/src/runtime/roadmap/facts.js +479 -0
- package/src/runtime/roadmap/gates.js +270 -0
- package/src/runtime/roadmap/loop.js +1636 -0
- package/src/runtime/roadmap/merge.js +381 -0
- package/src/runtime/roadmap/rows.js +1113 -0
- package/src/runtime/roadmap/seed.js +951 -0
- package/src/runtime/rules-report.js +336 -0
- package/src/runtime/scaffold/artifacts.js +60 -9
- package/src/runtime/scaffold/generate.js +21 -9
- package/src/runtime/settings/autopilot.js +106 -0
- package/src/runtime/settings/cli.js +280 -0
- package/src/runtime/settings/config-write.js +314 -0
- package/src/runtime/settings/files.js +271 -0
- package/src/runtime/settings/legacy.js +154 -0
- package/src/runtime/settings/lexical.js +56 -0
- package/src/runtime/settings/prepared-run.js +269 -0
- package/src/runtime/settings/project-layers.js +118 -0
- package/src/runtime/settings/refs.js +288 -0
- package/src/runtime/settings/registry.js +553 -0
- package/src/runtime/settings/resolve.js +713 -0
- package/src/runtime/settings/shared.js +37 -0
- package/src/runtime/settings/worker-billing.js +107 -0
- package/src/runtime/settings/write.js +269 -0
- package/src/runtime/slot-command.js +78 -0
- package/src/runtime/slots.js +671 -0
- package/src/runtime/source-version.js +1 -0
- package/src/runtime/state-root.js +217 -0
- package/src/runtime/storage/cli.js +12 -5
- package/src/runtime/storage/engine.js +9 -2
- package/src/runtime/storage/registry.js +14 -1
- package/src/runtime/storage/roots.js +66 -16
- package/src/runtime/storage/rules.js +82 -10
- package/src/runtime/storage/upkeep.js +10 -2
- package/src/runtime/storage/work-status.js +245 -26
- package/src/runtime/task-attempts.js +589 -69
- package/src/runtime/task-classifier.js +47 -11
- package/src/runtime/task-graph.js +373 -0
- package/src/runtime/task-run.js +778 -0
- package/src/runtime/task-scope.js +176 -55
- package/src/runtime/task-sources.js +147 -0
- package/src/runtime/task-state.js +374 -64
- package/src/runtime/team/admission.js +301 -0
- package/src/runtime/team/envelope.js +324 -0
- package/src/runtime/team/review-guard.js +156 -0
- package/src/runtime/trace/analyze.js +427 -0
- package/src/runtime/trace/baseline.js +770 -0
- package/src/runtime/trace/ledger.js +63 -140
- package/src/runtime/trace/otlp-export.js +568 -0
- package/src/runtime/trace/otlp-map.js +481 -0
- package/src/runtime/trace/spans.js +745 -0
- package/src/runtime/trace/telemetry.js +225 -0
- package/src/runtime/trace/views.js +1 -0
- package/src/runtime/transcripts/command.js +98 -0
- package/src/runtime/transcripts/cursor.js +326 -0
- package/src/runtime/transcripts/read.js +126 -0
- package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
- package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
- package/src/runtime/transcripts/readers/copilot-events.js +90 -0
- package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
- package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
- package/src/runtime/transcripts/registry.js +161 -0
- package/src/runtime/transcripts/stats.js +161 -0
- package/src/runtime/verification/check-key.js +628 -0
- package/src/runtime/verification/contract-runner.js +546 -11
- package/src/runtime/verification/engine.js +499 -9
- package/src/runtime/verification/impact.js +617 -0
- package/src/runtime/verification/inflight.js +205 -0
- package/src/runtime/verification/registry.js +176 -0
- package/src/runtime/verification/reuse.js +184 -0
- package/src/runtime/verification/telemetry-emit.js +254 -0
- package/src/runtime/verification/test-selection.js +67 -7
- package/src/runtime/worker/active.js +90 -0
- package/src/runtime/worker/cancel.js +222 -0
- package/src/runtime/worker/catalogue.js +122 -0
- package/src/runtime/worker/command.js +677 -0
- package/src/runtime/worker/evidence.js +288 -0
- package/src/runtime/worker/launch.js +288 -0
- package/src/runtime/worker/models.js +137 -0
- package/src/runtime/worker/parsers.js +218 -0
- package/src/runtime/worker/probes/index.js +706 -0
- package/src/runtime/worker/process-tree.js +415 -0
- package/src/runtime/worker/quota.js +43 -0
- package/src/runtime/worker/readiness.js +713 -0
- package/src/runtime/worker/run-store.js +166 -0
- package/src/runtime/worker/supervisor.js +541 -0
- package/src/runtime/worker/worktree-check.js +252 -0
- package/src/runtime/workflow-engine.js +5 -1
- package/src/runtime/workflow-orchestrator.js +578 -63
- package/src/runtime/worktree.js +149 -22
- package/src/state/index.js +1 -1
- package/src/state/lockfile.js +64 -10
- package/src/state/run-lock.js +1 -1
- package/types/node-commonjs.d.ts +106 -7
- package/core/harnesses/gemini/hooks/hooks.json +0 -77
- package/core/harnesses/gemini/hooks/lib.sh +0 -302
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
- package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
- package/core/harnesses/gemini/hooks/session-end.sh +0 -5
- package/core/harnesses/gemini/hooks/session-start.sh +0 -7
- package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
- package/core/shared/guidance/FLAGS.md +0 -25
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
- package/src/adapters/gemini/hooks.js +0 -251
- package/src/adapters/gemini/index.js +0 -429
|
@@ -11,7 +11,7 @@ const path = require('node:path');
|
|
|
11
11
|
* @param {{ k?: number }} [options]
|
|
12
12
|
* @returns {Array<{ id: string, rrfScore: number }>}
|
|
13
13
|
*/
|
|
14
|
-
function reciprocalRankFusion(rankedLists , { k = 60 }
|
|
14
|
+
function reciprocalRankFusion(rankedLists , { k = 60, fullPrecision = false } = {}) {
|
|
15
15
|
const scores = new Map ();
|
|
16
16
|
const items = new Map ();
|
|
17
17
|
|
|
@@ -26,9 +26,7 @@ function reciprocalRankFusion(rankedLists , { k = 60 } =
|
|
|
26
26
|
if (!id) continue;
|
|
27
27
|
const rrf = 1 / (k + rank + 1);
|
|
28
28
|
scores.set(id, (scores.get(id) || 0) + rrf);
|
|
29
|
-
if (!items.has(id))
|
|
30
|
-
items.set(id, item);
|
|
31
|
-
}
|
|
29
|
+
if (!items.has(id)) items.set(id, item);
|
|
32
30
|
}
|
|
33
31
|
}
|
|
34
32
|
|
|
@@ -37,7 +35,7 @@ function reciprocalRankFusion(rankedLists , { k = 60 } =
|
|
|
37
35
|
fused.push({
|
|
38
36
|
...items.get(id),
|
|
39
37
|
id,
|
|
40
|
-
rrfScore: Number(rrfScore.toFixed(6)),
|
|
38
|
+
rrfScore: fullPrecision ? rrfScore : Number(rrfScore.toFixed(6)),
|
|
41
39
|
});
|
|
42
40
|
}
|
|
43
41
|
|
|
@@ -51,21 +49,22 @@ function reciprocalRankFusion(rankedLists , { k = 60 } =
|
|
|
51
49
|
* @param {string} [projectRoot]
|
|
52
50
|
* @returns {{ file: string, line?: number, endLine?: number }}
|
|
53
51
|
*/
|
|
52
|
+
const slashed = (p ) => p.split(path.sep).join('/');
|
|
54
53
|
function normalizeLocator(raw , projectRoot = process.cwd()) {
|
|
55
54
|
if (!raw) return { file: '' };
|
|
56
55
|
if (typeof raw === 'string') {
|
|
57
56
|
const match = raw.match(/^([^:]+)(?::(\d+))?(?::(\d+))?$/);
|
|
58
57
|
if (match) {
|
|
59
|
-
const relFile = path.isAbsolute(match[1]) ? path.relative(projectRoot, match[1]) : match[1];
|
|
58
|
+
const relFile = slashed(path.isAbsolute(match[1]) ? path.relative(projectRoot, match[1]) : match[1]);
|
|
60
59
|
return {
|
|
61
60
|
file: relFile,
|
|
62
61
|
...(match[2] ? { line: parseInt(match[2], 10) } : {}),
|
|
63
62
|
};
|
|
64
63
|
}
|
|
65
|
-
return { file: path.isAbsolute(raw) ? path.relative(projectRoot, raw) : raw };
|
|
64
|
+
return { file: slashed(path.isAbsolute(raw) ? path.relative(projectRoot, raw) : raw) };
|
|
66
65
|
}
|
|
67
66
|
if (typeof raw === 'object' && raw.file) {
|
|
68
|
-
const relFile = path.isAbsolute(raw.file) ? path.relative(projectRoot, raw.file) : raw.file;
|
|
67
|
+
const relFile = slashed(path.isAbsolute(raw.file) ? path.relative(projectRoot, raw.file) : raw.file);
|
|
69
68
|
return {
|
|
70
69
|
file: relFile,
|
|
71
70
|
...(raw.line ? { line: raw.line } : {}),
|
|
@@ -142,8 +141,137 @@ function fuseHybridResults({
|
|
|
142
141
|
return candidates;
|
|
143
142
|
}
|
|
144
143
|
|
|
144
|
+
/** Strict code candidates are separate from the legacy guidance locator string convention. */
|
|
145
|
+
function normalizeCodeCandidates(items , { projectRoot = process.cwd(), provider, capability, corpusIdentity = '', queryRef = null, retrievalAttemptRef = null } = {}) {
|
|
146
|
+
const { relativeCodePath } = require('./code-corpus.js');
|
|
147
|
+
const candidates = [];
|
|
148
|
+
const rejected = [];
|
|
149
|
+
for (const item of Array.isArray(items) ? items : []) {
|
|
150
|
+
let raw = item?.locator || item?.path || item?.file;
|
|
151
|
+
let loc ;
|
|
152
|
+
if (typeof raw === 'string') {
|
|
153
|
+
const match = /^(.*?):(\d+)(?::(\d+))?$/.exec(raw);
|
|
154
|
+
if (match) loc = { file: match[1], line: Number(match[2]), endLine: Number(match[3] || match[2]) };
|
|
155
|
+
else loc = { file: raw, line: item.startLine ?? item.line ?? 1, endLine: item.endLine ?? item.startLine ?? item.line ?? 1 };
|
|
156
|
+
} else if (raw && typeof raw === 'object') {
|
|
157
|
+
loc = { file: raw.file, line: raw.line ?? raw.startLine ?? 1, endLine: raw.endLine ?? raw.line ?? raw.startLine ?? 1 };
|
|
158
|
+
}
|
|
159
|
+
if (loc && typeof loc.file === 'string' && path.isAbsolute(loc.file)) loc.file = path.relative(projectRoot, loc.file);
|
|
160
|
+
const file = relativeCodePath(loc?.file);
|
|
161
|
+
if (!file || !Number.isInteger(loc.line) || !Number.isInteger(loc.endLine) || loc.line < 1 || loc.endLine < loc.line) {
|
|
162
|
+
rejected.push({ locator: raw ?? null, reason: 'invalid-locator' }); continue;
|
|
163
|
+
}
|
|
164
|
+
loc.file = file;
|
|
165
|
+
const id = JSON.stringify([corpusIdentity, file, loc.line, loc.endLine]);
|
|
166
|
+
const actualProvider = item.provider || provider;
|
|
167
|
+
const actualCapability = item.capability || capability;
|
|
168
|
+
const origin = { provider: actualProvider, capability: actualCapability, queryRef: item.queryRef ?? queryRef,
|
|
169
|
+
retrievalAttemptRef: item.retrievalAttemptRef ?? retrievalAttemptRef,
|
|
170
|
+
originalRank: candidates.length + 1, sourceVersion: item.sourceVersion ?? null };
|
|
171
|
+
const origins = item.origins?.length ? item.origins.map((o ) => ({ ...origin, ...o })) : [origin];
|
|
172
|
+
candidates.push({ ...item, schemaVersion: 1, id, locator: loc, provider: actualProvider, capability: actualCapability, origins });
|
|
173
|
+
}
|
|
174
|
+
return { candidates, rejected };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function addOrigins(bucket , origins ) {
|
|
178
|
+
for (const origin of origins) {
|
|
179
|
+
const serialized = JSON.stringify(origin);
|
|
180
|
+
if (!bucket.some((o ) => JSON.stringify(o) === serialized)) bucket.push(origin);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** RRF terms summed in ascending-rank order, so equal rank multisets give bit-identical scores (DEC-016). */
|
|
185
|
+
function rrfSum(ranks , k ) {
|
|
186
|
+
return [...ranks].sort((a, b) => a - b).reduce((sum, rank) => sum + 1 / (k + rank), 0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* File-grain fusion for conceptual requests with more than one voting provider (IC-006, DEC-025). A provider's
|
|
191
|
+
* rank for a file is its dense rank over distinct files; it votes only within its first `depth` files. Voted
|
|
192
|
+
* files order by capped RRF, then full-depth RRF, then path; files outside every provider's depth form an
|
|
193
|
+
* unscored tail ordered by smallest provider rank, then path. The delivered locator is the widest of each
|
|
194
|
+
* provider's first-ranked span on the file (ties: better provider rank, smaller line, endLine, provider id),
|
|
195
|
+
* always a span some provider returned.
|
|
196
|
+
*/
|
|
197
|
+
function fuseByFile(providerLists , { corpusIdentity, k, depth, compareText } ) {
|
|
198
|
+
const files = new Map ();
|
|
199
|
+
for (const [provider, list] of providerLists) {
|
|
200
|
+
let rank = 0;
|
|
201
|
+
for (const item of list) {
|
|
202
|
+
const file = item.locator.file;
|
|
203
|
+
if (!files.has(file)) files.set(file, { votes: [], origins: [] });
|
|
204
|
+
const entry = files.get(file) ;
|
|
205
|
+
if (!entry.votes.some((vote ) => vote.provider === provider)) { rank += 1; entry.votes.push({ provider, rank, item }); }
|
|
206
|
+
const locator = { file, line: item.locator.line, endLine: item.locator.endLine };
|
|
207
|
+
addOrigins(entry.origins, item.origins.map((origin ) => ({ ...origin, locator })));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const width = (vote ) => vote.item.locator.endLine - vote.item.locator.line;
|
|
211
|
+
const fused = [...files.entries()].map(([file, { votes, origins }]) => {
|
|
212
|
+
const ranks = votes.map((vote ) => vote.rank);
|
|
213
|
+
const [delivered] = [...votes].sort((a , b ) => width(b) - width(a) || a.rank - b.rank
|
|
214
|
+
|| a.item.locator.line - b.item.locator.line || a.item.locator.endLine - b.item.locator.endLine || compareText(a.provider, b.provider));
|
|
215
|
+
return { candidate: { ...delivered.item, id: JSON.stringify([corpusIdentity, file]), locator: { ...delivered.item.locator }, origins,
|
|
216
|
+
rrfScore: rrfSum(ranks.filter((rank ) => rank <= depth), k) },
|
|
217
|
+
file, full: rrfSum(ranks, k), best: Math.min(...ranks) };
|
|
218
|
+
});
|
|
219
|
+
fused.sort((a , b ) => {
|
|
220
|
+
const voted = Number(b.candidate.rrfScore > 0) - Number(a.candidate.rrfScore > 0);
|
|
221
|
+
if (voted) return voted;
|
|
222
|
+
if (a.candidate.rrfScore > 0) return b.candidate.rrfScore - a.candidate.rrfScore || b.full - a.full || compareText(a.file, b.file);
|
|
223
|
+
return a.best - b.best || compareText(a.file, b.file);
|
|
224
|
+
});
|
|
225
|
+
return fused.map((entry ) => entry.candidate);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* One vote per provider and identity, all origins retained, full precision until presentation. Span grain
|
|
230
|
+
* (exact requests, single-provider lists) keeps `(corpus identity, file, line, endLine)`; `grain: 'file'`
|
|
231
|
+
* with two or more voting providers fuses by file with vote depth `depth` (IC-006, DEC-025).
|
|
232
|
+
*/
|
|
233
|
+
function fuseRankedCandidates({ rankedLists, projectRoot = process.cwd(), corpusIdentity = '', k = 60, grain = 'span', depth = Infinity } ) {
|
|
234
|
+
const { compareText } = require('./code-corpus.js');
|
|
235
|
+
const providerLists = new Map ();
|
|
236
|
+
const items = [];
|
|
237
|
+
const rejected = [];
|
|
238
|
+
for (let listIndex = 0; listIndex < rankedLists.length; listIndex += 1) {
|
|
239
|
+
const list = rankedLists[listIndex];
|
|
240
|
+
const normalized = normalizeCodeCandidates(Array.isArray(list) ? list : list.candidates,
|
|
241
|
+
{ projectRoot, corpusIdentity, provider: list.provider || 'list-' + listIndex, capability: list.capability,
|
|
242
|
+
queryRef: list.queryRef, retrievalAttemptRef: list.retrievalAttemptRef });
|
|
243
|
+
rejected.push(...normalized.rejected);
|
|
244
|
+
for (const item of normalized.candidates) {
|
|
245
|
+
if (!providerLists.has(item.provider)) providerLists.set(item.provider, []);
|
|
246
|
+
providerLists.get(item.provider) .push(item);
|
|
247
|
+
items.push(item);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (grain === 'file' && providerLists.size > 1) {
|
|
251
|
+
const candidates = fuseByFile(providerLists, { corpusIdentity, k, depth, compareText });
|
|
252
|
+
candidates.forEach((candidate , index ) => { candidate.rank = index + 1; });
|
|
253
|
+
return { candidates, rejected };
|
|
254
|
+
}
|
|
255
|
+
const origins = new Map ();
|
|
256
|
+
for (const item of items) {
|
|
257
|
+
if (!origins.has(item.id)) origins.set(item.id, []);
|
|
258
|
+
addOrigins(origins.get(item.id) , item.origins);
|
|
259
|
+
}
|
|
260
|
+
const uniqueProviderLists = [...providerLists.values()].map((list ) => {
|
|
261
|
+
const seen = new Set ();
|
|
262
|
+
return list.filter((item ) => { if (seen.has(item.id)) return false; seen.add(item.id); return true; });
|
|
263
|
+
});
|
|
264
|
+
const candidates = reciprocalRankFusion(uniqueProviderLists, { k, fullPrecision: true }).map((item ) => ({ ...item, origins: origins.get(item.id) }));
|
|
265
|
+
candidates.sort((a , b ) => b.rrfScore - a.rrfScore || compareText(a.locator.file, b.locator.file)
|
|
266
|
+
|| a.locator.line - b.locator.line || a.locator.endLine - b.locator.endLine);
|
|
267
|
+
candidates.forEach((candidate , index ) => { candidate.rank = index + 1; });
|
|
268
|
+
return { candidates, rejected };
|
|
269
|
+
}
|
|
270
|
+
|
|
145
271
|
module.exports = {
|
|
146
272
|
reciprocalRankFusion,
|
|
147
273
|
normalizeLocator,
|
|
274
|
+
normalizeCodeCandidates,
|
|
275
|
+
fuseRankedCandidates,
|
|
148
276
|
fuseHybridResults,
|
|
149
277
|
};
|
|
@@ -145,7 +145,15 @@ function searchGuidance({ corpusDir, indexDir, query, k = 5, fsImpl = fs }
|
|
|
145
145
|
* code from this install, knowledge from the project being worked on. The `.doflow` root is
|
|
146
146
|
* $DOFLOW_CONFIG_DIR, else the nearest one above `stateRoot` that holds a guidance tree, else the
|
|
147
147
|
* home install; the index is kept beside the corpus chosen. */
|
|
148
|
-
function handleRetrieveCommand(
|
|
148
|
+
function handleRetrieveCommand(options = {}) {
|
|
149
|
+
const { query, top, json = false, stateRoot } = options;
|
|
150
|
+
if (options.scope !== undefined && options.scope !== 'guidance' && options.scope !== 'code') {
|
|
151
|
+
return usageError('retrieve', `--scope must be guidance or code, got '${options.scope}'`, json);
|
|
152
|
+
}
|
|
153
|
+
if (options.scope === 'code' || options.indexAction !== undefined) {
|
|
154
|
+
if (options.scope !== 'code') return usageError('retrieve', '--index-action requires --scope code', json);
|
|
155
|
+
return handleCodeRetrieveCommand(options);
|
|
156
|
+
}
|
|
149
157
|
const { configDir } = resolveRunsLocation({ start: stateRoot, containing: 'guidance' });
|
|
150
158
|
const corpusDir = path.join(configDir, 'guidance');
|
|
151
159
|
const indexDir = path.join(configDir, 'index', 'guidance');
|
|
@@ -174,6 +182,30 @@ function handleRetrieveCommand({ query, top, json = false, stateRoot }
|
|
|
174
182
|
}
|
|
175
183
|
}
|
|
176
184
|
|
|
185
|
+
/** Additive code path. The guidance branch above retains its existing output and exit behavior. The search root is
|
|
186
|
+
* `root` or cwd, never `stateRoot`: in a linked worktree the state root is the main checkout's feature store. */
|
|
187
|
+
function handleCodeRetrieveCommand(options ) {
|
|
188
|
+
const { executeCodeRetrieval } = require('./code-retrieval.js');
|
|
189
|
+
return executeCodeRetrieval({ ...options, root: options.root || process.cwd() }).then((outcome ) => {
|
|
190
|
+
const response = outcome.response;
|
|
191
|
+
if (options.json) console.log(JSON.stringify(response, null, 2));
|
|
192
|
+
else if (!response.ok) console.error(`[ERROR] retrieve: ${response.error}${response.cause ? ` (${response.cause})` : ''}${response.summary ? ` — ${response.summary}` : ''}`);
|
|
193
|
+
else if (response.action) console.log(`${response.complete ? '[OK]' : '[ERROR]'} ${response.action} code index${response.generation ? ` ${response.generation}` : ''}`);
|
|
194
|
+
else if (!response.results.length) console.log('[OK] No matching code found in the attempted scope. absenceEstablished=false');
|
|
195
|
+
else response.results.forEach((candidate , index ) => {
|
|
196
|
+
console.log(`${index + 1}. ${candidate.locator.file}:${candidate.locator.line}-${candidate.locator.endLine}`);
|
|
197
|
+
if (candidate.snippet) console.log(` ${candidate.snippet}`);
|
|
198
|
+
});
|
|
199
|
+
if (!options.json && response.gaps?.length) for (const gap of response.gaps) console.error(`[INFO] retrieve gap: ${gap.provider || gap.source || 'retrieval'} ${gap.reason}`);
|
|
200
|
+
return finishRuntime(outcome.exit);
|
|
201
|
+
}).catch((error ) => {
|
|
202
|
+
captureCaught(error, 'retrieve', 1);
|
|
203
|
+
if (options.json) console.log(JSON.stringify({ ok: false, schemaVersion: 1, scope: 'code', error: 'retrieval-failed', cause: error.message }, null, 2));
|
|
204
|
+
else console.error(`[ERROR] retrieve: ${error.message}`);
|
|
205
|
+
return finishRuntime(1);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
177
209
|
const {
|
|
178
210
|
reciprocalRankFusion,
|
|
179
211
|
normalizeLocator,
|
|
@@ -187,6 +219,7 @@ module.exports = {
|
|
|
187
219
|
buildImportGraph,
|
|
188
220
|
expandViaGraph,
|
|
189
221
|
handleRetrieveCommand,
|
|
222
|
+
handleCodeRetrieveCommand,
|
|
190
223
|
STOPWORDS,
|
|
191
224
|
reciprocalRankFusion,
|
|
192
225
|
normalizeLocator,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
function formatWithRTK(
|
|
12
|
+
resolution ,
|
|
13
|
+
options
|
|
14
|
+
) {
|
|
15
|
+
const rawJson = JSON.stringify(resolution, null, 2);
|
|
16
|
+
const rawChars = rawJson.length;
|
|
17
|
+
|
|
18
|
+
const lines = [];
|
|
19
|
+
const statusMark = resolution.status === 'HEALTHY' ? '✓' : resolution.status === 'FALLBACK' ? '▲' : '✗';
|
|
20
|
+
lines.push(`${statusMark} [${resolution.provider}] query: "${resolution.query}" (status: ${resolution.status}, freshness: ${resolution.freshness})`);
|
|
21
|
+
|
|
22
|
+
if (resolution.fallbackReason) {
|
|
23
|
+
lines.push(` fallback-reason: ${resolution.fallbackReason}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (resolution.isAbsence) {
|
|
27
|
+
lines.push(` absence: true (${resolution.absenceReason || 'zero occurrences'})`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const maxLines = options?.maxLines ?? 50;
|
|
31
|
+
const candidates = Array.isArray(resolution.candidates) ? resolution.candidates : [];
|
|
32
|
+
const totalCandidates = candidates.length;
|
|
33
|
+
const displayedCandidates = candidates.slice(0, maxLines);
|
|
34
|
+
|
|
35
|
+
for (const cand of displayedCandidates) {
|
|
36
|
+
const loc = cand.locator || {};
|
|
37
|
+
const fileLine = loc.line !== undefined ? `${loc.file}:${loc.line}` : loc.file;
|
|
38
|
+
const relStr = cand.relation ? ` [${cand.relation}]` : '';
|
|
39
|
+
const symStr = loc.symbol ? ` ${loc.symbol}` : '';
|
|
40
|
+
const scoreStr = typeof cand.score === 'number' ? ` (${cand.score.toFixed(2)})` : '';
|
|
41
|
+
lines.push(` #${cand.rank ?? 0} ${fileLine}${symStr}${relStr}${scoreStr}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (totalCandidates > maxLines) {
|
|
45
|
+
lines.push(` ... (${totalCandidates - maxLines} more candidates truncated)`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const formattedOutput = lines.join('\n');
|
|
49
|
+
const compressedChars = formattedOutput.length;
|
|
50
|
+
const ratio = rawChars > 0 ? Number((compressedChars / rawChars).toFixed(3)) : 1.0;
|
|
51
|
+
|
|
52
|
+
let locatorsPreserved = true;
|
|
53
|
+
for (const cand of displayedCandidates) {
|
|
54
|
+
const loc = cand.locator;
|
|
55
|
+
if (!loc || !loc.file) continue;
|
|
56
|
+
if (!formattedOutput.includes(loc.file)) {
|
|
57
|
+
locatorsPreserved = false;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (loc.line !== undefined && !formattedOutput.includes(`${loc.file}:${loc.line}`)) {
|
|
61
|
+
locatorsPreserved = false;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
rawChars,
|
|
68
|
+
compressedChars,
|
|
69
|
+
ratio,
|
|
70
|
+
formattedOutput,
|
|
71
|
+
locatorsPreserved,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
formatWithRTK,
|
|
77
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Semble 0.7.0 public API adapter. One bounded JSON request per process.
|
|
2
|
+
|
|
3
|
+
The audit policy denies network and child processes even on model loader retry paths.
|
|
4
|
+
It also confines Python writes to the invocation's declared project-local cache/staging.
|
|
5
|
+
No library function or private provider implementation is patched.
|
|
6
|
+
"""
|
|
7
|
+
import importlib.metadata
|
|
8
|
+
import json
|
|
9
|
+
import math
|
|
10
|
+
import os
|
|
11
|
+
import platform
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
VERSION = "0.7.0" # must equal SEMBLE_VERSION in semble-provider.ts (tested)
|
|
16
|
+
RUNTIME_PACKAGES = ("numpy", "model2vec", "tokenizers", "huggingface-hub", "safetensors", "vicinity")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def runtime_identity():
|
|
20
|
+
packages = {}
|
|
21
|
+
for name in RUNTIME_PACKAGES:
|
|
22
|
+
try:
|
|
23
|
+
packages[name] = importlib.metadata.version(name)
|
|
24
|
+
except importlib.metadata.PackageNotFoundError as error:
|
|
25
|
+
raise RuntimeError("provider-runtime-dependency-missing") from error
|
|
26
|
+
return {"python": platform.python_version(), "implementation": platform.python_implementation(),
|
|
27
|
+
"platform": platform.platform(), "machine": platform.machine(), "packages": packages}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def install_policy(roots):
|
|
31
|
+
roots = tuple(Path(root).resolve(strict=True) for root in roots)
|
|
32
|
+
|
|
33
|
+
def permitted(value):
|
|
34
|
+
if isinstance(value, int):
|
|
35
|
+
return True # already opened standard output/error descriptors
|
|
36
|
+
candidate = Path(os.fsdecode(value)).resolve()
|
|
37
|
+
return any(candidate == root or root in candidate.parents for root in roots)
|
|
38
|
+
|
|
39
|
+
def audit(event, args):
|
|
40
|
+
if event.startswith("socket.") or event in ("subprocess.Popen", "os.system", "os.exec", "os.posix_spawn", "os.fork"):
|
|
41
|
+
raise PermissionError("provider-network-or-subprocess-denied")
|
|
42
|
+
if event == "open":
|
|
43
|
+
file, mode, flags = args
|
|
44
|
+
writing = (isinstance(mode, str) and any(c in mode for c in "wax+")) or bool(flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))
|
|
45
|
+
if writing and not permitted(file):
|
|
46
|
+
raise PermissionError("provider-write-outside-state")
|
|
47
|
+
if event in ("os.mkdir", "os.remove", "os.rmdir", "os.chmod", "os.utime", "os.truncate") and not permitted(args[0]):
|
|
48
|
+
raise PermissionError("provider-write-outside-state")
|
|
49
|
+
if event in ("os.rename", "os.link", "os.symlink"):
|
|
50
|
+
if not permitted(args[0]) or not permitted(args[1]) or event != "os.rename":
|
|
51
|
+
raise PermissionError("provider-unsafe-mutation")
|
|
52
|
+
sys.addaudithook(audit)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run(request):
|
|
56
|
+
install_policy(request["allowedWriteRoots"])
|
|
57
|
+
try:
|
|
58
|
+
found = importlib.metadata.version("semble")
|
|
59
|
+
except importlib.metadata.PackageNotFoundError:
|
|
60
|
+
return {"ok": False, "reason": "provider-unavailable", "cause": f"semble is not installed for {sys.executable}"}
|
|
61
|
+
if found != VERSION:
|
|
62
|
+
return {"ok": False, "reason": "provider-version-mismatch", "cause": f"{sys.executable} has semble {found[:32]}; the bridge pins {VERSION}"}
|
|
63
|
+
from semble.index import SembleIndex
|
|
64
|
+
from semble.types import ContentType
|
|
65
|
+
if request["action"] == "query":
|
|
66
|
+
index = SembleIndex.load_from_disk(Path(request["indexPath"]))
|
|
67
|
+
count = request["poolSize"]
|
|
68
|
+
if index.stats.total_chunks != count:
|
|
69
|
+
raise ValueError("persisted chunk count changed")
|
|
70
|
+
results = index.search(request["query"], top_k=200, alpha=1.0, rerank=False)
|
|
71
|
+
candidates = []
|
|
72
|
+
for result in results:
|
|
73
|
+
chunk = result.chunk
|
|
74
|
+
score = float(result.score)
|
|
75
|
+
if not math.isfinite(score):
|
|
76
|
+
raise ValueError("nonfinite score")
|
|
77
|
+
candidates.append({"locator": {"file": chunk.file_path, "line": chunk.start_line, "endLine": chunk.end_line}, "score": score})
|
|
78
|
+
# Public scores are provider-native distinct ranks; sorting them cannot
|
|
79
|
+
# recover dense ties hidden inside Semble. Preserve the returned order.
|
|
80
|
+
return {"ok": True, "candidates": candidates, "poolSize": count}
|
|
81
|
+
if request["action"] not in ("build", "refresh"):
|
|
82
|
+
raise ValueError("invalid action")
|
|
83
|
+
index = SembleIndex.from_path(Path(request["mirror"]), model_path=request["modelPath"], content=(ContentType.CODE,), show_progress_bar=False)
|
|
84
|
+
index.save(Path(request["savePath"]))
|
|
85
|
+
return {"ok": True}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main():
|
|
89
|
+
try:
|
|
90
|
+
request = json.loads(sys.stdin.buffer.read(2097153))
|
|
91
|
+
result = run(request)
|
|
92
|
+
except importlib.metadata.PackageNotFoundError:
|
|
93
|
+
result = {"ok": False, "reason": "provider-unavailable"}
|
|
94
|
+
except (ImportError, AttributeError):
|
|
95
|
+
result = {"ok": False, "reason": "provider-api-unavailable"}
|
|
96
|
+
except Exception:
|
|
97
|
+
# Provider exception text may contain source/query data. Return only a fixed cause.
|
|
98
|
+
result = {"ok": False, "reason": "provider-load-or-operation-error"}
|
|
99
|
+
if result.get("ok"):
|
|
100
|
+
result["runtimeIdentity"] = runtime_identity()
|
|
101
|
+
result["version"] = VERSION
|
|
102
|
+
print(json.dumps(result, separators=(",", ":")))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
main()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const crypto = require('node:crypto');
|
|
6
|
+
const { SEMBLE_VERSION, SEMBLE_LIMITS, semanticStatePaths, fault, digest, safeDirectories, regularBytes,
|
|
7
|
+
inspectModel, validateInventory, readSource, checkSources, inspectArtifacts, acquireSemanticLock,
|
|
8
|
+
cleanupTemporary, executeBridge, readCompleted, scanTree, resolveSembleInterpreter, checkSembleVersion } = require('./semble-provider.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The in-progress marker never follows a link out of the state directory. A failed mutation leaves a regular
|
|
12
|
+
* marker behind on purpose, so a regular file is replaced; anything else (a symlink, a directory) is refused.
|
|
13
|
+
* The create is `wx` (O_CREAT|O_EXCL), which fails on any existing path, a symlink included, so a link planted
|
|
14
|
+
* between the unlink and the create is refused too rather than written through.
|
|
15
|
+
*/
|
|
16
|
+
function writeMarker(marker , body ) {
|
|
17
|
+
let found = null;
|
|
18
|
+
try { found = fs.lstatSync(marker); } catch (error ) { if (error.code !== 'ENOENT') throw error; }
|
|
19
|
+
if (found && !found.isFile()) throw fault('unsafe-state-file');
|
|
20
|
+
if (found) fs.unlinkSync(marker);
|
|
21
|
+
try { fs.writeFileSync(marker, body, { flag: 'wx', mode: 0o600 }); }
|
|
22
|
+
catch (error ) { if (error.code === 'EEXIST') throw fault('unsafe-state-file'); throw error; }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Explicit operations only. A failed mutation deliberately keeps its in-progress marker. */
|
|
26
|
+
async function runSembleLifecycle(options ) {
|
|
27
|
+
const started = Date.now(); const limits = { ...SEMBLE_LIMITS, ...options.limits };
|
|
28
|
+
const { inventory, action } = options; let release ; let staging ;
|
|
29
|
+
let added = 0; let changed = 0; let deleted = 0; let changedBytes = 0;
|
|
30
|
+
const checkDeadline = () => { if (Date.now() - started > limits.lifecycleMs) throw fault('lifecycle-timeout'); };
|
|
31
|
+
try {
|
|
32
|
+
if (action !== 'build' && action !== 'refresh') throw fault('invalid-index-action');
|
|
33
|
+
validateInventory(inventory, limits); const model = inspectModel(options.modelPath, limits);
|
|
34
|
+
const python = options.python || resolveSembleInterpreter().python; checkSembleVersion(python); // preflight: before any lock or mutation
|
|
35
|
+
const paths = semanticStatePaths(inventory); release = await acquireSemanticLock(paths); cleanupTemporary(paths);
|
|
36
|
+
let previous = null;
|
|
37
|
+
if (action === 'refresh') {
|
|
38
|
+
previous = readCompleted(paths, inventory, model, limits, true);
|
|
39
|
+
const oldInventory = { ...inventory, files: previous.files };
|
|
40
|
+
const artifacts = inspectArtifacts(paths.index, oldInventory, paths, model, limits);
|
|
41
|
+
if (artifacts.artifactIdentity !== previous.artifactIdentity) throw fault('incompatible-refresh-index');
|
|
42
|
+
}
|
|
43
|
+
const old = new Map((previous?.files || []).map((entry ) => [entry.path, entry]));
|
|
44
|
+
const current = new Set(inventory.files.map((entry ) => entry.path));
|
|
45
|
+
for (const entry of inventory.files) {
|
|
46
|
+
const prior = old.get(entry.path);
|
|
47
|
+
if (!prior) { added++; changedBytes += entry.sizeBytes; }
|
|
48
|
+
else if (prior.fileHash !== entry.fileHash) { changed++; changedBytes += entry.sizeBytes; }
|
|
49
|
+
}
|
|
50
|
+
deleted = [...old.keys()].filter((file ) => !current.has(file)).length;
|
|
51
|
+
if (action === 'refresh' && (added + changed + deleted > limits.changedFiles || changedBytes > limits.changedBytes)) throw fault('refresh-limit');
|
|
52
|
+
checkSources(inventory, limits); checkDeadline();
|
|
53
|
+
staging = fs.mkdtempSync(path.join(paths.state, 'staging-'));
|
|
54
|
+
const sourceStage = path.join(staging, 'source'); fs.mkdirSync(sourceStage);
|
|
55
|
+
// Stage verified ordinary copies before invalidating or modifying the stable mirror.
|
|
56
|
+
for (const entry of inventory.files) {
|
|
57
|
+
checkDeadline();
|
|
58
|
+
if (old.has(entry.path) && (old.get(entry.path) ).fileHash === entry.fileHash) continue;
|
|
59
|
+
const dest = path.join(sourceStage, entry.path); safeDirectories(sourceStage, path.dirname(dest), true);
|
|
60
|
+
fs.writeFileSync(dest, readSource(inventory, entry, limits), { flag: 'wx' });
|
|
61
|
+
}
|
|
62
|
+
checkSources(inventory, limits); checkDeadline();
|
|
63
|
+
writeMarker(paths.marker, JSON.stringify({ schemaVersion: 1, action, startedAt: new Date().toISOString() }));
|
|
64
|
+
if (action === 'build') {
|
|
65
|
+
// A repair build explicitly discards incompatible cache; never let from_path silently reuse it.
|
|
66
|
+
for (const dir of [paths.mirror, paths.cache]) if (fs.existsSync(dir)) { scanTree(dir, limits.artifactBytes); fs.rmSync(dir, { recursive: true }); }
|
|
67
|
+
}
|
|
68
|
+
safeDirectories(paths.state, paths.mirror, true); safeDirectories(paths.state, paths.cache, true);
|
|
69
|
+
for (const file of old.keys() ) if (!current.has(file)) fs.unlinkSync(path.join(paths.mirror, file));
|
|
70
|
+
for (const entry of inventory.files) {
|
|
71
|
+
checkDeadline(); const staged = path.join(sourceStage, entry.path);
|
|
72
|
+
if (!fs.existsSync(staged)) continue;
|
|
73
|
+
const dest = path.join(paths.mirror, entry.path); safeDirectories(paths.mirror, path.dirname(dest), true);
|
|
74
|
+
let previousMtime = 0; try { previousMtime = fs.lstatSync(dest).mtimeMs; } catch {}
|
|
75
|
+
fs.renameSync(staged, dest);
|
|
76
|
+
// A hash change must be newer than both the prior copy and saved provider time, even on coarse filesystems.
|
|
77
|
+
const seconds = Math.max(Date.now() / 1000, previousMtime / 1000 + 2, (previous?.providerTime || 0) + 2);
|
|
78
|
+
fs.utimesSync(dest, seconds, seconds);
|
|
79
|
+
}
|
|
80
|
+
const temp = path.join(staging, 'provider'); fs.mkdirSync(temp);
|
|
81
|
+
const savePath = path.join(staging, 'index');
|
|
82
|
+
const result = await executeBridge({ action, mirror: paths.mirror, modelPath: model.path, savePath, indexPath: paths.index,
|
|
83
|
+
allowedWriteRoots: [staging, paths.cache] }, { ...options, python, limits: { ...limits, lifecycleMs: Math.max(1, limits.lifecycleMs - (Date.now() - started)) } }, paths, temp);
|
|
84
|
+
if (!result.ok) throw fault(result.reason || 'provider-error', 'UNKNOWN', typeof result.cause === 'string' ? result.cause : undefined);
|
|
85
|
+
if (!result.runtimeIdentity || typeof result.runtimeIdentity !== 'object') throw fault('provider-runtime-identity-unavailable');
|
|
86
|
+
const artifacts = inspectArtifacts(savePath, inventory, paths, model, limits);
|
|
87
|
+
checkSources(inventory, limits); checkDeadline();
|
|
88
|
+
if (inspectModel(options.modelPath, limits).identity !== model.identity) throw fault('model-changed', 'STALE');
|
|
89
|
+
const generation = crypto.randomUUID();
|
|
90
|
+
safeDirectories(paths.state, path.dirname(paths.index), true);
|
|
91
|
+
const backup = path.join(staging, 'previous-index');
|
|
92
|
+
if (fs.existsSync(paths.index)) fs.renameSync(paths.index, backup);
|
|
93
|
+
fs.renameSync(savePath, paths.index);
|
|
94
|
+
const metadata = JSON.parse(regularBytes(path.join(paths.index, 'metadata.json'), limits.artifactBytes).toString('utf8'));
|
|
95
|
+
const manifest = { schemaVersion: 1, complete: true, providerVersion: SEMBLE_VERSION, runtimeIdentity: result.runtimeIdentity, root: inventory.root,
|
|
96
|
+
scopeIgnoreDigest: inventory.scopeIgnoreDigest, corpusIdentity: inventory.identity, model, generation,
|
|
97
|
+
providerTime: metadata.time, artifactIdentity: artifacts.artifactIdentity,
|
|
98
|
+
files: inventory.files.map((entry ) => ({ path: entry.path, sizeBytes: entry.sizeBytes, fileHash: entry.fileHash })) };
|
|
99
|
+
const pending = path.join(staging, 'manifest.json'); fs.writeFileSync(pending, JSON.stringify(manifest));
|
|
100
|
+
checkSources(inventory, limits); checkDeadline();
|
|
101
|
+
fs.renameSync(pending, paths.manifest); // Completion metadata is always the last published artifact.
|
|
102
|
+
fs.unlinkSync(paths.marker);
|
|
103
|
+
return { ok: true, action, complete: true, freshness: 'FRESH', generation, added, changed, deleted, skipped: 0,
|
|
104
|
+
bytes: changedBytes, elapsedMs: Date.now() - started, chunks: artifacts.chunks, artifactBytes: artifacts.artifactBytes };
|
|
105
|
+
} catch (error ) {
|
|
106
|
+
const reason = error.reason || 'lifecycle-error';
|
|
107
|
+
return { ok: false, action, complete: false, freshness: error.freshness || 'UNKNOWN', reason, ...(typeof error.cause === 'string' ? { cause: error.cause } : {}),
|
|
108
|
+
added, changed, deleted, skipped: inventory?.skipped?.length || 0, bytes: changedBytes, elapsedMs: Date.now() - started,
|
|
109
|
+
// A build needs the same lock, so contention is never repaired by "run a build".
|
|
110
|
+
repair: reason === 'lock-contention' ? 'Wait for the other semantic index operation to finish, then run this command again (see cause).'
|
|
111
|
+
: 'Run an explicit bounded build with a verified local model after correcting the cause.' };
|
|
112
|
+
} finally { try { if (staging) fs.rmSync(staging, { recursive: true, force: true }); } catch {} try { if (release) release(); } catch {} }
|
|
113
|
+
}
|
|
114
|
+
module.exports = { runSembleLifecycle };
|