@voybio/ace-swarm 0.1.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/CHANGELOG.md +109 -0
- package/LICENSE +186 -0
- package/README.md +229 -0
- package/assets/.agents/ACE/ACE-Init/AGENTS.md +210 -0
- package/assets/.agents/ACE/ACE-Init/instructions.md +118 -0
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +154 -0
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +216 -0
- package/assets/.agents/ACE/AGENT_REGISTRY.md +70 -0
- package/assets/.agents/ACE/AGENT_REGISTRY_7.md +9 -0
- package/assets/.agents/ACE/DIRECTIVE_KERNEL.md +234 -0
- package/assets/.agents/ACE/UI/AGENTS.md +115 -0
- package/assets/.agents/ACE/UI/instructions.md +178 -0
- package/assets/.agents/ACE/VOS/ACE_VOS_MISSING_INFO_MATRIX.md +42 -0
- package/assets/.agents/ACE/VOS/AGENTS.md +72 -0
- package/assets/.agents/ACE/VOS/instructions.md +211 -0
- package/assets/.agents/ACE/agent-astgrep/AGENTS.md +123 -0
- package/assets/.agents/ACE/agent-astgrep/instructions.md +91 -0
- package/assets/.agents/ACE/agent-builder/AGENTS.md +172 -0
- package/assets/.agents/ACE/agent-builder/instructions.md +137 -0
- package/assets/.agents/ACE/agent-docs/AGENTS.md +159 -0
- package/assets/.agents/ACE/agent-docs/instructions.md +133 -0
- package/assets/.agents/ACE/agent-eval/AGENTS.md +46 -0
- package/assets/.agents/ACE/agent-eval/instructions.md +56 -0
- package/assets/.agents/ACE/agent-memory/AGENTS.md +49 -0
- package/assets/.agents/ACE/agent-memory/instructions.md +50 -0
- package/assets/.agents/ACE/agent-observability/AGENTS.md +46 -0
- package/assets/.agents/ACE/agent-observability/instructions.md +50 -0
- package/assets/.agents/ACE/agent-ops/AGENTS.md +201 -0
- package/assets/.agents/ACE/agent-ops/instructions.md +136 -0
- package/assets/.agents/ACE/agent-qa/AGENTS.md +189 -0
- package/assets/.agents/ACE/agent-qa/instructions.md +121 -0
- package/assets/.agents/ACE/agent-release/AGENTS.md +48 -0
- package/assets/.agents/ACE/agent-release/instructions.md +49 -0
- package/assets/.agents/ACE/agent-research/AGENTS.md +160 -0
- package/assets/.agents/ACE/agent-research/instructions.md +118 -0
- package/assets/.agents/ACE/agent-security/AGENTS.md +48 -0
- package/assets/.agents/ACE/agent-security/instructions.md +50 -0
- package/assets/.agents/ACE/agent-skeptic/AGENTS.md +178 -0
- package/assets/.agents/ACE/agent-skeptic/instructions.md +196 -0
- package/assets/.agents/ACE/agent-spec/AGENTS.md +169 -0
- package/assets/.agents/ACE/agent-spec/instructions.md +116 -0
- package/assets/.agents/ACE/orchestrator/AGENTS.md +365 -0
- package/assets/.agents/ACE/orchestrator/instructions.md +231 -0
- package/assets/.agents/skills/ace-orchestrator/SKILL.md +63 -0
- package/assets/.agents/skills/ace-orchestrator/references/engineering-bootstrap-playbook.md +360 -0
- package/assets/.agents/skills/astgrep-index/SKILL.md +58 -0
- package/assets/.agents/skills/codemunch/SKILL.md +65 -0
- package/assets/.agents/skills/codemunch/references/ast-driven-protocol.md +543 -0
- package/assets/.agents/skills/codesnipe/SKILL.md +64 -0
- package/assets/.agents/skills/codesnipe/references/dual-codebase-playbook.md +671 -0
- package/assets/.agents/skills/eval-harness/SKILL.md +203 -0
- package/assets/.agents/skills/handoff-lint/SKILL.md +164 -0
- package/assets/.agents/skills/incident-commander/SKILL.md +174 -0
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +68 -0
- package/assets/.agents/skills/memory-curator/SKILL.md +179 -0
- package/assets/.agents/skills/problem-triage/SKILL.md +57 -0
- package/assets/.agents/skills/problem-triage/agents/openai.yaml +3 -0
- package/assets/.agents/skills/release-sentry/SKILL.md +189 -0
- package/assets/.agents/skills/risk-quant/SKILL.md +190 -0
- package/assets/.agents/skills/schema-forge/SKILL.md +174 -0
- package/assets/.agents/skills/skill-auditor/SKILL.md +52 -0
- package/assets/.agents/skills/state-auditor/SKILL.md +182 -0
- package/assets/.github/hooks/ace-copilot.json +68 -0
- package/assets/agent-state/ACE_WORKFLOW.md +131 -0
- package/assets/agent-state/ARTIFACT_MANIFEST.json +5 -0
- package/assets/agent-state/AST_GREP_COMMANDS.md +121 -0
- package/assets/agent-state/AST_GREP_INDEX.json +13 -0
- package/assets/agent-state/AST_GREP_INDEX.md +15 -0
- package/assets/agent-state/DECISIONS.md +7 -0
- package/assets/agent-state/EVIDENCE_LOG.md +7 -0
- package/assets/agent-state/HANDOFF.json +24 -0
- package/assets/agent-state/INTERFACE_REGISTRY.md +75 -0
- package/assets/agent-state/MODULES/gates/gate-autonomy.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-completeness.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-correctness.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-evaluation.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-operability.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-security.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +7 -0
- package/assets/agent-state/MODULES/registry.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-astgrep.json +49 -0
- package/assets/agent-state/MODULES/roles/capability-build.json +39 -0
- package/assets/agent-state/MODULES/roles/capability-docs.json +38 -0
- package/assets/agent-state/MODULES/roles/capability-eval.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-memory.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-observability.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-ops.json +45 -0
- package/assets/agent-state/MODULES/roles/capability-qa.json +40 -0
- package/assets/agent-state/MODULES/roles/capability-release.json +21 -0
- package/assets/agent-state/MODULES/roles/capability-research.json +44 -0
- package/assets/agent-state/MODULES/roles/capability-security.json +21 -0
- package/assets/agent-state/MODULES/roles/capability-skeptic.json +48 -0
- package/assets/agent-state/MODULES/roles/capability-spec.json +42 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +289 -0
- package/assets/agent-state/MODULES/schemas/ARTIFACT_MANIFEST.schema.json +185 -0
- package/assets/agent-state/MODULES/schemas/HANDOFF.agent-state.schema.json +124 -0
- package/assets/agent-state/MODULES/schemas/HANDOFF.schema.json +55 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +290 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +144 -0
- package/assets/agent-state/MODULES/schemas/STATUS_EVENT.schema.json +84 -0
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +138 -0
- package/assets/agent-state/MODULES/schemas/TRACKER_SNAPSHOT.schema.json +134 -0
- package/assets/agent-state/MODULES/schemas/VERICIFY_BRIDGE_SNAPSHOT.schema.json +157 -0
- package/assets/agent-state/MODULES/schemas/VERICIFY_PROCESS_POST_LOG.schema.json +93 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +133 -0
- package/assets/agent-state/PROVENANCE_LOG.md +28 -0
- package/assets/agent-state/QUALITY_GATES.md +15 -0
- package/assets/agent-state/RISKS.md +8 -0
- package/assets/agent-state/SCOPE.md +20 -0
- package/assets/agent-state/SKILL_CATALOG.md +48 -0
- package/assets/agent-state/STATUS.md +8 -0
- package/assets/agent-state/STATUS_EVENTS.ndjson +1 -0
- package/assets/agent-state/TASK.md +18 -0
- package/assets/agent-state/TEAL_CONFIG.md +117 -0
- package/assets/agent-state/handoff-registry.json +5 -0
- package/assets/agent-state/index-fingerprints.json +7 -0
- package/assets/agent-state/index.json +32 -0
- package/assets/agent-state/run-ledger.json +5 -0
- package/assets/agent-state/runtime-executor-sessions.json +5 -0
- package/assets/agent-state/runtime-tool-specs.json +5 -0
- package/assets/agent-state/runtime-workspaces.json +5 -0
- package/assets/agent-state/todo-state.json +7 -0
- package/assets/agent-state/tracker-snapshot.json +7 -0
- package/assets/agent-state/vericify/ace-bridge.json +60 -0
- package/assets/agent-state/vericify/process-posts.json +5 -0
- package/assets/instructions/ACE.instructions.md +187 -0
- package/assets/instructions/ACE_Coder.instructions.md +146 -0
- package/assets/instructions/ACE_UI.instructions.md +178 -0
- package/assets/instructions/ACE_VOS.instructions.md +211 -0
- package/assets/scripts/ace-hook-dispatch.mjs +538 -0
- package/assets/scripts/bootstrap-workspace.sh +27 -0
- package/assets/scripts/copilot-hook-dispatch.mjs +3 -0
- package/assets/scripts/eval-harness.sh +68 -0
- package/assets/scripts/render-mcp-configs.sh +396 -0
- package/assets/tasks/README.md +48 -0
- package/assets/tasks/SWARM_HANDOFF.example.json +53 -0
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +55 -0
- package/assets/tasks/SWARM_HANDOFF.example_vos_to_ui.json +55 -0
- package/assets/tasks/SWARM_HANDOFF.template.json +52 -0
- package/assets/tasks/cli_work_split.md +22 -0
- package/assets/tasks/lessons.md +17 -0
- package/assets/tasks/role_tasks.md +206 -0
- package/assets/tasks/todo.md +23 -0
- package/dist/ace-autonomy.d.ts +137 -0
- package/dist/ace-autonomy.js +472 -0
- package/dist/ace-context.d.ts +29 -0
- package/dist/ace-context.js +240 -0
- package/dist/ace-internal-tools.d.ts +8 -0
- package/dist/ace-internal-tools.js +76 -0
- package/dist/ace-server-instructions.d.ts +12 -0
- package/dist/ace-server-instructions.js +324 -0
- package/dist/agent-runtime/role-adapters.d.ts +29 -0
- package/dist/agent-runtime/role-adapters.js +573 -0
- package/dist/astgrep-index.d.ts +24 -0
- package/dist/astgrep-index.js +476 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +591 -0
- package/dist/git-ops.d.ts +53 -0
- package/dist/git-ops.js +238 -0
- package/dist/handoff-registry.d.ts +71 -0
- package/dist/handoff-registry.js +422 -0
- package/dist/helpers.d.ts +126 -0
- package/dist/helpers.js +1687 -0
- package/dist/index-store.d.ts +51 -0
- package/dist/index-store.js +328 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/internal-tool-runtime.d.ts +21 -0
- package/dist/internal-tool-runtime.js +136 -0
- package/dist/job-scheduler.d.ts +175 -0
- package/dist/job-scheduler.js +1217 -0
- package/dist/kanban.d.ts +27 -0
- package/dist/kanban.js +339 -0
- package/dist/local-model-runtime.d.ts +40 -0
- package/dist/local-model-runtime.js +174 -0
- package/dist/model-bridge.d.ts +54 -0
- package/dist/model-bridge.js +587 -0
- package/dist/orchestrator-supervisor.d.ts +100 -0
- package/dist/orchestrator-supervisor.js +399 -0
- package/dist/problem-triage.d.ts +23 -0
- package/dist/problem-triage.js +448 -0
- package/dist/prompts.d.ts +7 -0
- package/dist/prompts.js +628 -0
- package/dist/public-surface.d.ts +30 -0
- package/dist/public-surface.js +316 -0
- package/dist/resources.d.ts +7 -0
- package/dist/resources.js +545 -0
- package/dist/run-ledger.d.ts +36 -0
- package/dist/run-ledger.js +257 -0
- package/dist/runtime-command.d.ts +18 -0
- package/dist/runtime-command.js +76 -0
- package/dist/runtime-executor.d.ts +104 -0
- package/dist/runtime-executor.js +985 -0
- package/dist/runtime-profile.d.ts +116 -0
- package/dist/runtime-profile.js +532 -0
- package/dist/runtime-tool-specs.d.ts +68 -0
- package/dist/runtime-tool-specs.js +527 -0
- package/dist/safe-edit.d.ts +52 -0
- package/dist/safe-edit.js +255 -0
- package/dist/schemas.d.ts +44 -0
- package/dist/schemas.js +830 -0
- package/dist/semantic-cache.d.ts +147 -0
- package/dist/semantic-cache.js +552 -0
- package/dist/semantic-hash.d.ts +83 -0
- package/dist/semantic-hash.js +346 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +46 -0
- package/dist/shared.d.ts +136 -0
- package/dist/shared.js +269 -0
- package/dist/skill-auditor.d.ts +26 -0
- package/dist/skill-auditor.js +184 -0
- package/dist/skill-catalog.d.ts +60 -0
- package/dist/skill-catalog.js +305 -0
- package/dist/status-events.d.ts +40 -0
- package/dist/status-events.js +269 -0
- package/dist/store/ace-packed-store.d.ts +69 -0
- package/dist/store/ace-packed-store.js +434 -0
- package/dist/store/bootstrap-store.d.ts +46 -0
- package/dist/store/bootstrap-store.js +242 -0
- package/dist/store/catalog-builder.d.ts +21 -0
- package/dist/store/catalog-builder.js +68 -0
- package/dist/store/importer.d.ts +19 -0
- package/dist/store/importer.js +157 -0
- package/dist/store/knowledge-bake.d.ts +59 -0
- package/dist/store/knowledge-bake.js +339 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +25 -0
- package/dist/store/materializers/hook-context-materializer.js +100 -0
- package/dist/store/materializers/host-file-materializer.d.ts +37 -0
- package/dist/store/materializers/host-file-materializer.js +271 -0
- package/dist/store/materializers/todo-syncer.d.ts +30 -0
- package/dist/store/materializers/todo-syncer.js +140 -0
- package/dist/store/materializers/vericify-projector.d.ts +38 -0
- package/dist/store/materializers/vericify-projector.js +239 -0
- package/dist/store/repositories/discovery-repository.d.ts +24 -0
- package/dist/store/repositories/discovery-repository.js +58 -0
- package/dist/store/repositories/handoff-repository.d.ts +31 -0
- package/dist/store/repositories/handoff-repository.js +67 -0
- package/dist/store/repositories/ledger-repository.d.ts +26 -0
- package/dist/store/repositories/ledger-repository.js +49 -0
- package/dist/store/repositories/runtime-kv-repository.d.ts +16 -0
- package/dist/store/repositories/runtime-kv-repository.js +36 -0
- package/dist/store/repositories/scheduler-repository.d.ts +50 -0
- package/dist/store/repositories/scheduler-repository.js +123 -0
- package/dist/store/repositories/session-repository.d.ts +33 -0
- package/dist/store/repositories/session-repository.js +82 -0
- package/dist/store/repositories/todo-repository.d.ts +31 -0
- package/dist/store/repositories/todo-repository.js +77 -0
- package/dist/store/repositories/tracker-repository.d.ts +25 -0
- package/dist/store/repositories/tracker-repository.js +43 -0
- package/dist/store/repositories/vericify-repository.d.ts +32 -0
- package/dist/store/repositories/vericify-repository.js +58 -0
- package/dist/store/skills-install.d.ts +28 -0
- package/dist/store/skills-install.js +86 -0
- package/dist/store/state-reader.d.ts +49 -0
- package/dist/store/state-reader.js +111 -0
- package/dist/store/store-artifacts.d.ts +12 -0
- package/dist/store/store-artifacts.js +138 -0
- package/dist/store/store-snapshot.d.ts +19 -0
- package/dist/store/store-snapshot.js +140 -0
- package/dist/store/topology-bake.d.ts +15 -0
- package/dist/store/topology-bake.js +215 -0
- package/dist/store/types.d.ts +155 -0
- package/dist/store/types.js +35 -0
- package/dist/store/workspace-snapshot.d.ts +26 -0
- package/dist/store/workspace-snapshot.js +107 -0
- package/dist/store/write-queue.d.ts +7 -0
- package/dist/store/write-queue.js +26 -0
- package/dist/todo-state.d.ts +41 -0
- package/dist/todo-state.js +399 -0
- package/dist/tools-agent.d.ts +7 -0
- package/dist/tools-agent.js +1542 -0
- package/dist/tools-discovery.d.ts +6 -0
- package/dist/tools-discovery.js +178 -0
- package/dist/tools-drift.d.ts +13 -0
- package/dist/tools-drift.js +357 -0
- package/dist/tools-files.d.ts +6 -0
- package/dist/tools-files.js +679 -0
- package/dist/tools-framework.d.ts +7 -0
- package/dist/tools-framework.js +1414 -0
- package/dist/tools-git.d.ts +6 -0
- package/dist/tools-git.js +183 -0
- package/dist/tools-handoff.d.ts +32 -0
- package/dist/tools-handoff.js +489 -0
- package/dist/tools-lifecycle.d.ts +6 -0
- package/dist/tools-lifecycle.js +205 -0
- package/dist/tools-memory.d.ts +6 -0
- package/dist/tools-memory.js +260 -0
- package/dist/tools-scheduler.d.ts +6 -0
- package/dist/tools-scheduler.js +228 -0
- package/dist/tools-skills.d.ts +3 -0
- package/dist/tools-skills.js +104 -0
- package/dist/tools-todo.d.ts +6 -0
- package/dist/tools-todo.js +154 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.js +33 -0
- package/dist/tracker-adapters.d.ts +74 -0
- package/dist/tracker-adapters.js +776 -0
- package/dist/tracker-sync.d.ts +10 -0
- package/dist/tracker-sync.js +84 -0
- package/dist/tui/agent-runner.d.ts +137 -0
- package/dist/tui/agent-runner.js +466 -0
- package/dist/tui/agent-worker.d.ts +10 -0
- package/dist/tui/agent-worker.js +347 -0
- package/dist/tui/chat.d.ts +84 -0
- package/dist/tui/chat.js +368 -0
- package/dist/tui/commands.d.ts +57 -0
- package/dist/tui/commands.js +432 -0
- package/dist/tui/dashboard.d.ts +24 -0
- package/dist/tui/dashboard.js +110 -0
- package/dist/tui/index.d.ts +114 -0
- package/dist/tui/index.js +1059 -0
- package/dist/tui/input.d.ts +49 -0
- package/dist/tui/input.js +336 -0
- package/dist/tui/layout.d.ts +116 -0
- package/dist/tui/layout.js +367 -0
- package/dist/tui/ollama.d.ts +116 -0
- package/dist/tui/ollama.js +192 -0
- package/dist/tui/openai-compatible.d.ts +63 -0
- package/dist/tui/openai-compatible.js +370 -0
- package/dist/tui/provider-discovery.d.ts +59 -0
- package/dist/tui/provider-discovery.js +530 -0
- package/dist/tui/renderer.d.ts +166 -0
- package/dist/tui/renderer.js +304 -0
- package/dist/tui/tabs.d.ts +70 -0
- package/dist/tui/tabs.js +208 -0
- package/dist/tui/telemetry.d.ts +56 -0
- package/dist/tui/telemetry.js +106 -0
- package/dist/vericify-bridge.d.ts +146 -0
- package/dist/vericify-bridge.js +571 -0
- package/dist/vericify-context.d.ts +10 -0
- package/dist/vericify-context.js +72 -0
- package/dist/workspace-manager.d.ts +107 -0
- package/dist/workspace-manager.js +636 -0
- package/package.json +83 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic cache — snapshot, persist, and diff agent-state documents
|
|
3
|
+
* using MinHash signatures for approximate similarity.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. takeSnapshot() — hash + sign every tracked file, persist to .tmp/cache/
|
|
7
|
+
* 2. computeSemanticDelta() — compare two snapshots, emit per-file + per-section deltas
|
|
8
|
+
* 3. checkDriftBudget() — gate: is the delta within acceptable bounds?
|
|
9
|
+
*
|
|
10
|
+
* Integration points:
|
|
11
|
+
* • scan_workspace_delta (coarse gate) runs first, flags changed files
|
|
12
|
+
* • semantic cache runs only on files flagged changed
|
|
13
|
+
* • drift report feeds skeptic/ops via status events
|
|
14
|
+
*/
|
|
15
|
+
export interface MinHashParams {
|
|
16
|
+
num_permutations: number;
|
|
17
|
+
shingle_size: number;
|
|
18
|
+
seed: number;
|
|
19
|
+
}
|
|
20
|
+
export interface FileSignature {
|
|
21
|
+
content_hash: string;
|
|
22
|
+
size: number;
|
|
23
|
+
signature: number[];
|
|
24
|
+
sections: SectionSignature[];
|
|
25
|
+
}
|
|
26
|
+
export interface SectionSignature {
|
|
27
|
+
heading: string;
|
|
28
|
+
line_start: number;
|
|
29
|
+
line_end: number;
|
|
30
|
+
content_hash: string;
|
|
31
|
+
signature: number[];
|
|
32
|
+
}
|
|
33
|
+
export interface SemanticSnapshot {
|
|
34
|
+
version: 1;
|
|
35
|
+
snapshot_id: string;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
minhash_params: MinHashParams;
|
|
38
|
+
tracked_dirs: string[];
|
|
39
|
+
file_count: number;
|
|
40
|
+
total_size: number;
|
|
41
|
+
files: Record<string, FileSignature>;
|
|
42
|
+
}
|
|
43
|
+
export type FileChangeStatus = "unchanged" | "modified" | "added" | "deleted";
|
|
44
|
+
export interface SectionChange {
|
|
45
|
+
heading: string;
|
|
46
|
+
status: FileChangeStatus;
|
|
47
|
+
similarity: number;
|
|
48
|
+
diff_summary?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface FileDelta {
|
|
51
|
+
status: FileChangeStatus;
|
|
52
|
+
file_similarity: number;
|
|
53
|
+
size_before: number;
|
|
54
|
+
size_after: number;
|
|
55
|
+
growth_pct: number;
|
|
56
|
+
section_changes: SectionChange[];
|
|
57
|
+
}
|
|
58
|
+
export interface DriftBudgetConfig {
|
|
59
|
+
max_growth_pct: number;
|
|
60
|
+
max_churn_pct: number;
|
|
61
|
+
max_new_files: number;
|
|
62
|
+
max_deleted_files: number;
|
|
63
|
+
}
|
|
64
|
+
export interface DriftBudgetResult {
|
|
65
|
+
passed: boolean;
|
|
66
|
+
actual_growth_pct: number;
|
|
67
|
+
actual_churn_pct: number;
|
|
68
|
+
new_files: number;
|
|
69
|
+
deleted_files: number;
|
|
70
|
+
violations: string[];
|
|
71
|
+
}
|
|
72
|
+
export interface SemanticDelta {
|
|
73
|
+
version: 1;
|
|
74
|
+
before_snapshot: string;
|
|
75
|
+
after_snapshot: string;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
summary: {
|
|
78
|
+
unchanged_files: number;
|
|
79
|
+
modified_files: number;
|
|
80
|
+
added_files: number;
|
|
81
|
+
deleted_files: number;
|
|
82
|
+
total_section_changes: number;
|
|
83
|
+
};
|
|
84
|
+
drift_budget?: DriftBudgetResult;
|
|
85
|
+
files: Record<string, FileDelta>;
|
|
86
|
+
}
|
|
87
|
+
export interface TakeSnapshotInput {
|
|
88
|
+
snapshot_id: string;
|
|
89
|
+
tracked_dirs?: string[];
|
|
90
|
+
shingle_size?: number;
|
|
91
|
+
num_permutations?: number;
|
|
92
|
+
seed?: number;
|
|
93
|
+
/** Only snapshot these specific files (overrides tracked_dirs scan). */
|
|
94
|
+
files?: string[];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Snapshot current state of all tracked files into semantic cache.
|
|
98
|
+
* Computes content hashes + MinHash signatures at file and section level.
|
|
99
|
+
*/
|
|
100
|
+
export declare function takeSnapshot(input: TakeSnapshotInput): SemanticSnapshot;
|
|
101
|
+
export declare function loadSnapshot(snapshotId: string): SemanticSnapshot | undefined;
|
|
102
|
+
export declare function listSnapshots(): string[];
|
|
103
|
+
export interface ComputeDeltaInput {
|
|
104
|
+
before: string | SemanticSnapshot;
|
|
105
|
+
after: string | SemanticSnapshot;
|
|
106
|
+
/** Include surgical token diff for modified sections (more expensive). */
|
|
107
|
+
include_diffs?: boolean;
|
|
108
|
+
/** Drift budget config. */
|
|
109
|
+
drift_budget?: DriftBudgetConfig;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Compare two snapshots and produce a semantic delta.
|
|
113
|
+
* For markdown files: section-level alignment via DP.
|
|
114
|
+
* For all files: file-level MinHash similarity.
|
|
115
|
+
*/
|
|
116
|
+
export declare function computeSemanticDelta(input: ComputeDeltaInput): SemanticDelta;
|
|
117
|
+
/**
|
|
118
|
+
* Pre-cycle: take a "before" snapshot.
|
|
119
|
+
* Returns the snapshot ID for use in post-cycle comparison.
|
|
120
|
+
*/
|
|
121
|
+
export declare function preCycleSnapshot(cycleId: string, trackedDirs?: string[]): SemanticSnapshot;
|
|
122
|
+
/**
|
|
123
|
+
* Post-cycle: take an "after" snapshot and compute the delta.
|
|
124
|
+
*/
|
|
125
|
+
export declare function postCycleSnapshot(cycleId: string, trackedDirs?: string[], driftBudget?: DriftBudgetConfig, includeDiffs?: boolean): {
|
|
126
|
+
snapshot: SemanticSnapshot;
|
|
127
|
+
delta: SemanticDelta;
|
|
128
|
+
};
|
|
129
|
+
export interface RewriteTarget {
|
|
130
|
+
file: string;
|
|
131
|
+
type: "full" | "section";
|
|
132
|
+
/** For section targets: heading text for ast-grep/heading search. */
|
|
133
|
+
heading?: string;
|
|
134
|
+
/** Line range (0-based) of the affected section. */
|
|
135
|
+
line_start?: number;
|
|
136
|
+
line_end?: number;
|
|
137
|
+
/** Similarity to old version. */
|
|
138
|
+
similarity: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Extract rewrite targets from a semantic delta.
|
|
142
|
+
* Returns only modified or added targets — nothing for unchanged/deleted.
|
|
143
|
+
* For code files: full-file target (use ast-grep node targeting externally).
|
|
144
|
+
* For markdown: section-level targets with heading/line ranges.
|
|
145
|
+
*/
|
|
146
|
+
export declare function extractRewriteTargets(delta: SemanticDelta): RewriteTarget[];
|
|
147
|
+
//# sourceMappingURL=semantic-cache.d.ts.map
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic cache — snapshot, persist, and diff agent-state documents
|
|
3
|
+
* using MinHash signatures for approximate similarity.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. takeSnapshot() — hash + sign every tracked file, persist to .tmp/cache/
|
|
7
|
+
* 2. computeSemanticDelta() — compare two snapshots, emit per-file + per-section deltas
|
|
8
|
+
* 3. checkDriftBudget() — gate: is the delta within acceptable bounds?
|
|
9
|
+
*
|
|
10
|
+
* Integration points:
|
|
11
|
+
* • scan_workspace_delta (coarse gate) runs first, flags changed files
|
|
12
|
+
* • semantic cache runs only on files flagged changed
|
|
13
|
+
* • drift report feeds skeptic/ops via status events
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { resolve } from "node:path";
|
|
18
|
+
import { MinHash, getShingles, splitMarkdownSections, surgicalTokenDiff, renderDiffSummary, } from "./semantic-hash.js";
|
|
19
|
+
import { WORKSPACE_ROOT, safeRead } from "./helpers.js";
|
|
20
|
+
import { isReadError, normalizeRelPath } from "./shared.js";
|
|
21
|
+
// ────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Constants
|
|
23
|
+
// ────────────────────────────────────────────────────────────────────
|
|
24
|
+
const CACHE_DIR = ".tmp/cache/semantic";
|
|
25
|
+
const DEFAULT_SHINGLE_SIZE = 3;
|
|
26
|
+
const DEFAULT_NUM_PERMUTATIONS = 128;
|
|
27
|
+
const DEFAULT_SEED = 42;
|
|
28
|
+
const DEFAULT_SIMILARITY_THRESHOLD = 0.4;
|
|
29
|
+
/** Tracked directories (workspace-relative). */
|
|
30
|
+
const DEFAULT_TRACKED_DIRS = ["agent-state", "global-state", "engineering-state", "venture-state"];
|
|
31
|
+
/** File extensions to track for semantic cache. */
|
|
32
|
+
const TRACKED_EXTENSIONS = new Set([".md", ".json", ".yaml", ".yml", ".txt"]);
|
|
33
|
+
/** Max file size for semantic hashing (skip huge files). */
|
|
34
|
+
const MAX_FILE_SIZE = 512 * 1024; // 512 KB
|
|
35
|
+
// ────────────────────────────────────────────────────────────────────
|
|
36
|
+
// Helpers
|
|
37
|
+
// ────────────────────────────────────────────────────────────────────
|
|
38
|
+
function contentHash(content) {
|
|
39
|
+
return createHash("sha256").update(content).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
function cacheDir() {
|
|
42
|
+
const dir = resolve(WORKSPACE_ROOT, CACHE_DIR);
|
|
43
|
+
if (!existsSync(dir))
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
function snapshotPath(snapshotId) {
|
|
48
|
+
return resolve(cacheDir(), `${snapshotId}.json`);
|
|
49
|
+
}
|
|
50
|
+
function isMarkdown(path) {
|
|
51
|
+
return path.endsWith(".md");
|
|
52
|
+
}
|
|
53
|
+
function isTrackedFile(name) {
|
|
54
|
+
const dot = name.lastIndexOf(".");
|
|
55
|
+
if (dot <= 0)
|
|
56
|
+
return false;
|
|
57
|
+
return TRACKED_EXTENSIONS.has(name.slice(dot).toLowerCase());
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Recursively collect tracked files from given directories.
|
|
61
|
+
* Returns workspace-relative paths.
|
|
62
|
+
*/
|
|
63
|
+
function collectTrackedFiles(dirs, maxDepth = 4) {
|
|
64
|
+
const files = [];
|
|
65
|
+
function walk(absDir, relDir, depth) {
|
|
66
|
+
if (depth > maxDepth || !existsSync(absDir))
|
|
67
|
+
return;
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = readdirSync(absDir, { withFileTypes: true });
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (entry.name.startsWith("."))
|
|
77
|
+
continue;
|
|
78
|
+
const entryAbs = resolve(absDir, entry.name);
|
|
79
|
+
const entryRel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
80
|
+
if (entry.isDirectory()) {
|
|
81
|
+
// Skip known junk dirs
|
|
82
|
+
if (["node_modules", "dist", ".git", "HANDOFF_HISTORY"].includes(entry.name))
|
|
83
|
+
continue;
|
|
84
|
+
walk(entryAbs, entryRel, depth + 1);
|
|
85
|
+
}
|
|
86
|
+
else if (entry.isFile() && isTrackedFile(entry.name)) {
|
|
87
|
+
try {
|
|
88
|
+
const stat = readFileSync(entryAbs);
|
|
89
|
+
if (stat.length <= MAX_FILE_SIZE) {
|
|
90
|
+
files.push(entryRel);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// skip unreadable
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const dir of dirs) {
|
|
100
|
+
const absDir = resolve(WORKSPACE_ROOT, dir);
|
|
101
|
+
walk(absDir, dir, 0);
|
|
102
|
+
}
|
|
103
|
+
return files.sort();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Snapshot current state of all tracked files into semantic cache.
|
|
107
|
+
* Computes content hashes + MinHash signatures at file and section level.
|
|
108
|
+
*/
|
|
109
|
+
export function takeSnapshot(input) {
|
|
110
|
+
const shingleSize = input.shingle_size ?? DEFAULT_SHINGLE_SIZE;
|
|
111
|
+
const numPerms = input.num_permutations ?? DEFAULT_NUM_PERMUTATIONS;
|
|
112
|
+
const seed = input.seed ?? DEFAULT_SEED;
|
|
113
|
+
const trackedDirs = input.tracked_dirs ?? DEFAULT_TRACKED_DIRS;
|
|
114
|
+
const mh = new MinHash(numPerms, seed);
|
|
115
|
+
// Collect files
|
|
116
|
+
const filePaths = input.files ?? collectTrackedFiles(trackedDirs);
|
|
117
|
+
const fileSignatures = {};
|
|
118
|
+
let totalSize = 0;
|
|
119
|
+
for (const relPath of filePaths) {
|
|
120
|
+
const raw = safeRead(relPath);
|
|
121
|
+
if (isReadError(raw))
|
|
122
|
+
continue;
|
|
123
|
+
const hash = contentHash(raw);
|
|
124
|
+
const size = Buffer.byteLength(raw, "utf-8");
|
|
125
|
+
totalSize += size;
|
|
126
|
+
// File-level signature
|
|
127
|
+
const fileShingles = getShingles(raw, shingleSize);
|
|
128
|
+
const fileSig = mh.computeSignature(fileShingles);
|
|
129
|
+
// Section-level signatures (markdown only)
|
|
130
|
+
const sections = [];
|
|
131
|
+
if (isMarkdown(relPath)) {
|
|
132
|
+
const mdSections = splitMarkdownSections(raw);
|
|
133
|
+
for (const sec of mdSections) {
|
|
134
|
+
const secShingles = getShingles(sec.content, shingleSize);
|
|
135
|
+
sections.push({
|
|
136
|
+
heading: sec.heading,
|
|
137
|
+
line_start: sec.lineStart,
|
|
138
|
+
line_end: sec.lineEnd,
|
|
139
|
+
content_hash: contentHash(sec.content),
|
|
140
|
+
signature: mh.computeSignature(secShingles),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
fileSignatures[normalizeRelPath(relPath)] = {
|
|
145
|
+
content_hash: hash,
|
|
146
|
+
size,
|
|
147
|
+
signature: fileSig,
|
|
148
|
+
sections,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const snapshot = {
|
|
152
|
+
version: 1,
|
|
153
|
+
snapshot_id: input.snapshot_id,
|
|
154
|
+
timestamp: new Date().toISOString(),
|
|
155
|
+
minhash_params: {
|
|
156
|
+
num_permutations: numPerms,
|
|
157
|
+
shingle_size: shingleSize,
|
|
158
|
+
seed,
|
|
159
|
+
},
|
|
160
|
+
tracked_dirs: trackedDirs,
|
|
161
|
+
file_count: Object.keys(fileSignatures).length,
|
|
162
|
+
total_size: totalSize,
|
|
163
|
+
files: fileSignatures,
|
|
164
|
+
};
|
|
165
|
+
// Persist
|
|
166
|
+
const outPath = snapshotPath(input.snapshot_id);
|
|
167
|
+
writeFileSync(outPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
168
|
+
return snapshot;
|
|
169
|
+
}
|
|
170
|
+
// ────────────────────────────────────────────────────────────────────
|
|
171
|
+
// Core: Load Snapshot
|
|
172
|
+
// ────────────────────────────────────────────────────────────────────
|
|
173
|
+
export function loadSnapshot(snapshotId) {
|
|
174
|
+
const path = snapshotPath(snapshotId);
|
|
175
|
+
if (!existsSync(path))
|
|
176
|
+
return undefined;
|
|
177
|
+
try {
|
|
178
|
+
const raw = readFileSync(path, "utf-8");
|
|
179
|
+
const parsed = JSON.parse(raw);
|
|
180
|
+
if (parsed.version !== 1)
|
|
181
|
+
return undefined;
|
|
182
|
+
return parsed;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export function listSnapshots() {
|
|
189
|
+
const dir = cacheDir();
|
|
190
|
+
try {
|
|
191
|
+
return readdirSync(dir)
|
|
192
|
+
.filter((f) => f.endsWith(".json"))
|
|
193
|
+
.map((f) => f.replace(/\.json$/, ""))
|
|
194
|
+
.sort();
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Compare two snapshots and produce a semantic delta.
|
|
202
|
+
* For markdown files: section-level alignment via DP.
|
|
203
|
+
* For all files: file-level MinHash similarity.
|
|
204
|
+
*/
|
|
205
|
+
export function computeSemanticDelta(input) {
|
|
206
|
+
const before = typeof input.before === "string"
|
|
207
|
+
? loadSnapshot(input.before)
|
|
208
|
+
: input.before;
|
|
209
|
+
const after = typeof input.after === "string" ? loadSnapshot(input.after) : input.after;
|
|
210
|
+
if (!before || !after) {
|
|
211
|
+
throw new Error(`Missing snapshot: before=${typeof input.before === "string" ? input.before : "inline"}, after=${typeof input.after === "string" ? input.after : "inline"}`);
|
|
212
|
+
}
|
|
213
|
+
// Verify MinHash params match (otherwise signatures are incomparable)
|
|
214
|
+
const paramMatch = before.minhash_params.seed === after.minhash_params.seed &&
|
|
215
|
+
before.minhash_params.num_permutations ===
|
|
216
|
+
after.minhash_params.num_permutations &&
|
|
217
|
+
before.minhash_params.shingle_size === after.minhash_params.shingle_size;
|
|
218
|
+
if (!paramMatch) {
|
|
219
|
+
throw new Error("MinHash parameters differ between snapshots — signatures are not comparable. " +
|
|
220
|
+
"Use the same seed/permutations/shingle_size for both.");
|
|
221
|
+
}
|
|
222
|
+
const mh = new MinHash(before.minhash_params.num_permutations, before.minhash_params.seed);
|
|
223
|
+
const allPaths = new Set([
|
|
224
|
+
...Object.keys(before.files),
|
|
225
|
+
...Object.keys(after.files),
|
|
226
|
+
]);
|
|
227
|
+
const fileDelta = {};
|
|
228
|
+
let unchangedCount = 0;
|
|
229
|
+
let modifiedCount = 0;
|
|
230
|
+
let addedCount = 0;
|
|
231
|
+
let deletedCount = 0;
|
|
232
|
+
let totalSectionChanges = 0;
|
|
233
|
+
for (const path of allPaths) {
|
|
234
|
+
const beforeFile = before.files[path];
|
|
235
|
+
const afterFile = after.files[path];
|
|
236
|
+
if (!beforeFile && afterFile) {
|
|
237
|
+
// Added
|
|
238
|
+
addedCount++;
|
|
239
|
+
fileDelta[path] = {
|
|
240
|
+
status: "added",
|
|
241
|
+
file_similarity: 0,
|
|
242
|
+
size_before: 0,
|
|
243
|
+
size_after: afterFile.size,
|
|
244
|
+
growth_pct: 100,
|
|
245
|
+
section_changes: afterFile.sections.map((s) => ({
|
|
246
|
+
heading: s.heading,
|
|
247
|
+
status: "added",
|
|
248
|
+
similarity: 0,
|
|
249
|
+
})),
|
|
250
|
+
};
|
|
251
|
+
totalSectionChanges += afterFile.sections.length;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (beforeFile && !afterFile) {
|
|
255
|
+
// Deleted
|
|
256
|
+
deletedCount++;
|
|
257
|
+
fileDelta[path] = {
|
|
258
|
+
status: "deleted",
|
|
259
|
+
file_similarity: 0,
|
|
260
|
+
size_before: beforeFile.size,
|
|
261
|
+
size_after: 0,
|
|
262
|
+
growth_pct: -100,
|
|
263
|
+
section_changes: beforeFile.sections.map((s) => ({
|
|
264
|
+
heading: s.heading,
|
|
265
|
+
status: "deleted",
|
|
266
|
+
similarity: 0,
|
|
267
|
+
})),
|
|
268
|
+
};
|
|
269
|
+
totalSectionChanges += beforeFile.sections.length;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
// Both exist — compare
|
|
273
|
+
if (beforeFile.content_hash === afterFile.content_hash) {
|
|
274
|
+
// Exact match
|
|
275
|
+
unchangedCount++;
|
|
276
|
+
fileDelta[path] = {
|
|
277
|
+
status: "unchanged",
|
|
278
|
+
file_similarity: 1.0,
|
|
279
|
+
size_before: beforeFile.size,
|
|
280
|
+
size_after: afterFile.size,
|
|
281
|
+
growth_pct: 0,
|
|
282
|
+
section_changes: [],
|
|
283
|
+
};
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
// Modified — compute similarity
|
|
287
|
+
modifiedCount++;
|
|
288
|
+
const fileSim = mh.estimateSimilarity(beforeFile.signature, afterFile.signature);
|
|
289
|
+
const growthPct = beforeFile.size > 0
|
|
290
|
+
? ((afterFile.size - beforeFile.size) / beforeFile.size) * 100
|
|
291
|
+
: 100;
|
|
292
|
+
// Section-level alignment for markdown files
|
|
293
|
+
const sectionChanges = [];
|
|
294
|
+
if (beforeFile.sections.length > 0 ||
|
|
295
|
+
afterFile.sections.length > 0) {
|
|
296
|
+
// Reconstruct MarkdownSection structures from stored signatures
|
|
297
|
+
// for alignment. We need the content for surgical diff though,
|
|
298
|
+
// so read the actual files if diffs are requested.
|
|
299
|
+
const beforeSections = beforeFile.sections.map((s) => ({
|
|
300
|
+
heading: s.heading,
|
|
301
|
+
lineStart: s.line_start,
|
|
302
|
+
lineEnd: s.line_end,
|
|
303
|
+
content: "", // placeholder — content not stored in cache
|
|
304
|
+
}));
|
|
305
|
+
const afterSections = afterFile.sections.map((s) => ({
|
|
306
|
+
heading: s.heading,
|
|
307
|
+
lineStart: s.line_start,
|
|
308
|
+
lineEnd: s.line_end,
|
|
309
|
+
content: "",
|
|
310
|
+
}));
|
|
311
|
+
// Use stored signatures for alignment instead of recomputing
|
|
312
|
+
// Build a custom alignment from section content hashes
|
|
313
|
+
const bSigs = beforeFile.sections;
|
|
314
|
+
const aSigs = afterFile.sections;
|
|
315
|
+
// Similarity matrix from pre-computed signatures
|
|
316
|
+
const simMatrix = Array.from({ length: bSigs.length }, () => new Array(aSigs.length).fill(0));
|
|
317
|
+
for (let i = 0; i < bSigs.length; i++) {
|
|
318
|
+
for (let j = 0; j < aSigs.length; j++) {
|
|
319
|
+
simMatrix[i][j] = mh.estimateSimilarity(bSigs[i].signature, aSigs[j].signature);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// DP alignment
|
|
323
|
+
const n = bSigs.length;
|
|
324
|
+
const m = aSigs.length;
|
|
325
|
+
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
326
|
+
for (let i = 1; i <= n; i++) {
|
|
327
|
+
for (let j = 1; j <= m; j++) {
|
|
328
|
+
if (simMatrix[i - 1][j - 1] > DEFAULT_SIMILARITY_THRESHOLD) {
|
|
329
|
+
dp[i][j] = 1 + dp[i - 1][j - 1];
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Backtrack
|
|
337
|
+
let ii = n;
|
|
338
|
+
let jj = m;
|
|
339
|
+
const aligned = [];
|
|
340
|
+
while (ii > 0 || jj > 0) {
|
|
341
|
+
if (ii > 0 &&
|
|
342
|
+
jj > 0 &&
|
|
343
|
+
simMatrix[ii - 1][jj - 1] > DEFAULT_SIMILARITY_THRESHOLD) {
|
|
344
|
+
const sim = simMatrix[ii - 1][jj - 1];
|
|
345
|
+
const isExact = bSigs[ii - 1].content_hash === aSigs[jj - 1].content_hash;
|
|
346
|
+
aligned.push({
|
|
347
|
+
status: isExact ? "unchanged" : "modified",
|
|
348
|
+
leftIdx: ii - 1,
|
|
349
|
+
rightIdx: jj - 1,
|
|
350
|
+
similarity: sim,
|
|
351
|
+
});
|
|
352
|
+
ii--;
|
|
353
|
+
jj--;
|
|
354
|
+
}
|
|
355
|
+
else if (jj > 0 &&
|
|
356
|
+
(ii === 0 || dp[ii][jj - 1] >= dp[ii - 1][jj])) {
|
|
357
|
+
aligned.push({
|
|
358
|
+
status: "added",
|
|
359
|
+
leftIdx: -1,
|
|
360
|
+
rightIdx: jj - 1,
|
|
361
|
+
similarity: 0,
|
|
362
|
+
});
|
|
363
|
+
jj--;
|
|
364
|
+
}
|
|
365
|
+
else if (ii > 0) {
|
|
366
|
+
aligned.push({
|
|
367
|
+
status: "deleted",
|
|
368
|
+
leftIdx: ii - 1,
|
|
369
|
+
rightIdx: -1,
|
|
370
|
+
similarity: 0,
|
|
371
|
+
});
|
|
372
|
+
ii--;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
aligned.reverse();
|
|
376
|
+
// Build section changes
|
|
377
|
+
for (const entry of aligned) {
|
|
378
|
+
const heading = entry.rightIdx >= 0
|
|
379
|
+
? aSigs[entry.rightIdx].heading
|
|
380
|
+
: bSigs[entry.leftIdx].heading;
|
|
381
|
+
const change = {
|
|
382
|
+
heading,
|
|
383
|
+
status: entry.status,
|
|
384
|
+
similarity: entry.similarity,
|
|
385
|
+
};
|
|
386
|
+
// Surgical diff for modified sections (if requested and content available)
|
|
387
|
+
if (input.include_diffs && entry.status === "modified") {
|
|
388
|
+
// Read actual file content for surgical diff
|
|
389
|
+
const beforeContent = safeRead(path);
|
|
390
|
+
const afterContent = safeRead(path);
|
|
391
|
+
if (!isReadError(beforeContent) && !isReadError(afterContent)) {
|
|
392
|
+
const bSections = splitMarkdownSections(beforeContent);
|
|
393
|
+
const aSections = splitMarkdownSections(afterContent);
|
|
394
|
+
const bSec = bSections[entry.leftIdx];
|
|
395
|
+
const aSec = aSections[entry.rightIdx];
|
|
396
|
+
if (bSec && aSec) {
|
|
397
|
+
const ops = surgicalTokenDiff(bSec.content, aSec.content);
|
|
398
|
+
change.diff_summary = renderDiffSummary(ops).slice(0, 2000);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
sectionChanges.push(change);
|
|
403
|
+
if (entry.status !== "unchanged")
|
|
404
|
+
totalSectionChanges++;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
fileDelta[path] = {
|
|
408
|
+
status: "modified",
|
|
409
|
+
file_similarity: fileSim,
|
|
410
|
+
size_before: beforeFile.size,
|
|
411
|
+
size_after: afterFile.size,
|
|
412
|
+
growth_pct: Math.round(growthPct * 100) / 100,
|
|
413
|
+
section_changes: sectionChanges,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
// Drift budget
|
|
417
|
+
let driftBudget;
|
|
418
|
+
if (input.drift_budget) {
|
|
419
|
+
driftBudget = checkDriftBudget({
|
|
420
|
+
before_total_size: before.total_size,
|
|
421
|
+
after_total_size: after.total_size,
|
|
422
|
+
modified_count: modifiedCount,
|
|
423
|
+
total_file_count: allPaths.size,
|
|
424
|
+
added_count: addedCount,
|
|
425
|
+
deleted_count: deletedCount,
|
|
426
|
+
}, input.drift_budget);
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
version: 1,
|
|
430
|
+
before_snapshot: before.snapshot_id,
|
|
431
|
+
after_snapshot: after.snapshot_id,
|
|
432
|
+
timestamp: new Date().toISOString(),
|
|
433
|
+
summary: {
|
|
434
|
+
unchanged_files: unchangedCount,
|
|
435
|
+
modified_files: modifiedCount,
|
|
436
|
+
added_files: addedCount,
|
|
437
|
+
deleted_files: deletedCount,
|
|
438
|
+
total_section_changes: totalSectionChanges,
|
|
439
|
+
},
|
|
440
|
+
drift_budget: driftBudget,
|
|
441
|
+
files: fileDelta,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const DEFAULT_DRIFT_BUDGET = {
|
|
445
|
+
max_growth_pct: 25,
|
|
446
|
+
max_churn_pct: 40,
|
|
447
|
+
max_new_files: 10,
|
|
448
|
+
max_deleted_files: 5,
|
|
449
|
+
};
|
|
450
|
+
function checkDriftBudget(metrics, config = DEFAULT_DRIFT_BUDGET) {
|
|
451
|
+
const growthPct = metrics.before_total_size > 0
|
|
452
|
+
? ((metrics.after_total_size - metrics.before_total_size) /
|
|
453
|
+
metrics.before_total_size) *
|
|
454
|
+
100
|
|
455
|
+
: metrics.after_total_size > 0
|
|
456
|
+
? 100
|
|
457
|
+
: 0;
|
|
458
|
+
const churnPct = metrics.total_file_count > 0
|
|
459
|
+
? (metrics.modified_count / metrics.total_file_count) * 100
|
|
460
|
+
: 0;
|
|
461
|
+
const violations = [];
|
|
462
|
+
if (Math.abs(growthPct) > config.max_growth_pct) {
|
|
463
|
+
violations.push(`Size growth ${growthPct.toFixed(1)}% exceeds ±${config.max_growth_pct}% limit`);
|
|
464
|
+
}
|
|
465
|
+
if (churnPct > config.max_churn_pct) {
|
|
466
|
+
violations.push(`Churn ${churnPct.toFixed(1)}% exceeds ${config.max_churn_pct}% limit`);
|
|
467
|
+
}
|
|
468
|
+
if (metrics.added_count > config.max_new_files) {
|
|
469
|
+
violations.push(`${metrics.added_count} new files exceeds limit of ${config.max_new_files}`);
|
|
470
|
+
}
|
|
471
|
+
if (metrics.deleted_count > config.max_deleted_files) {
|
|
472
|
+
violations.push(`${metrics.deleted_count} deleted files exceeds limit of ${config.max_deleted_files}`);
|
|
473
|
+
}
|
|
474
|
+
return {
|
|
475
|
+
passed: violations.length === 0,
|
|
476
|
+
actual_growth_pct: Math.round(growthPct * 100) / 100,
|
|
477
|
+
actual_churn_pct: Math.round(churnPct * 100) / 100,
|
|
478
|
+
new_files: metrics.added_count,
|
|
479
|
+
deleted_files: metrics.deleted_count,
|
|
480
|
+
violations,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
// ────────────────────────────────────────────────────────────────────
|
|
484
|
+
// Convenience: cycle wrapper
|
|
485
|
+
// ────────────────────────────────────────────────────────────────────
|
|
486
|
+
/**
|
|
487
|
+
* Pre-cycle: take a "before" snapshot.
|
|
488
|
+
* Returns the snapshot ID for use in post-cycle comparison.
|
|
489
|
+
*/
|
|
490
|
+
export function preCycleSnapshot(cycleId, trackedDirs) {
|
|
491
|
+
return takeSnapshot({
|
|
492
|
+
snapshot_id: `${cycleId}_pre`,
|
|
493
|
+
tracked_dirs: trackedDirs,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Post-cycle: take an "after" snapshot and compute the delta.
|
|
498
|
+
*/
|
|
499
|
+
export function postCycleSnapshot(cycleId, trackedDirs, driftBudget, includeDiffs = false) {
|
|
500
|
+
const afterSnap = takeSnapshot({
|
|
501
|
+
snapshot_id: `${cycleId}_post`,
|
|
502
|
+
tracked_dirs: trackedDirs,
|
|
503
|
+
});
|
|
504
|
+
const delta = computeSemanticDelta({
|
|
505
|
+
before: `${cycleId}_pre`,
|
|
506
|
+
after: afterSnap,
|
|
507
|
+
include_diffs: includeDiffs,
|
|
508
|
+
drift_budget: driftBudget ?? DEFAULT_DRIFT_BUDGET,
|
|
509
|
+
});
|
|
510
|
+
return { snapshot: afterSnap, delta };
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Extract rewrite targets from a semantic delta.
|
|
514
|
+
* Returns only modified or added targets — nothing for unchanged/deleted.
|
|
515
|
+
* For code files: full-file target (use ast-grep node targeting externally).
|
|
516
|
+
* For markdown: section-level targets with heading/line ranges.
|
|
517
|
+
*/
|
|
518
|
+
export function extractRewriteTargets(delta) {
|
|
519
|
+
const targets = [];
|
|
520
|
+
for (const [path, fileDelta] of Object.entries(delta.files)) {
|
|
521
|
+
if (fileDelta.status === "unchanged" || fileDelta.status === "deleted") {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
if (fileDelta.status === "added") {
|
|
525
|
+
targets.push({ file: path, type: "full", similarity: 0 });
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
// Modified
|
|
529
|
+
if (fileDelta.section_changes.length === 0) {
|
|
530
|
+
// Non-markdown or no section data: full-file target
|
|
531
|
+
targets.push({
|
|
532
|
+
file: path,
|
|
533
|
+
type: "full",
|
|
534
|
+
similarity: fileDelta.file_similarity,
|
|
535
|
+
});
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
// Section-level targets for markdown
|
|
539
|
+
for (const sec of fileDelta.section_changes) {
|
|
540
|
+
if (sec.status === "unchanged")
|
|
541
|
+
continue;
|
|
542
|
+
targets.push({
|
|
543
|
+
file: path,
|
|
544
|
+
type: "section",
|
|
545
|
+
heading: sec.heading,
|
|
546
|
+
similarity: sec.similarity,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return targets;
|
|
551
|
+
}
|
|
552
|
+
//# sourceMappingURL=semantic-cache.js.map
|