ctxora 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +441 -0
  3. package/README.vi.md +441 -0
  4. package/bin/ctxora.mjs +147 -0
  5. package/package.json +45 -0
  6. package/pyproject.toml +59 -0
  7. package/src/chunking/compressor.py +104 -0
  8. package/src/chunking/treesitter_chunker.py +240 -0
  9. package/src/compact/anthropic.py +98 -0
  10. package/src/compact/gemini.py +88 -0
  11. package/src/compact/handoff.py +179 -0
  12. package/src/compact/openai.py +318 -0
  13. package/src/compact/summarizer.py +186 -0
  14. package/src/context/assembler.py +298 -0
  15. package/src/context/budgeting.py +137 -0
  16. package/src/context/sanitizer.py +23 -0
  17. package/src/evaluation/__init__.py +1 -0
  18. package/src/evaluation/gates.py +172 -0
  19. package/src/evaluation/metrics.py +41 -0
  20. package/src/harness_context/__init__.py +5 -0
  21. package/src/harness_context/adapters/__init__.py +1 -0
  22. package/src/harness_context/adapters/clients/__init__.py +4 -0
  23. package/src/harness_context/adapters/clients/formatters.py +47 -0
  24. package/src/harness_context/adapters/clients/profiles.py +29 -0
  25. package/src/harness_context/adapters/ecc/__init__.py +4 -0
  26. package/src/harness_context/adapters/ecc/detection.py +41 -0
  27. package/src/harness_context/adapters/ecc/mapping.py +32 -0
  28. package/src/harness_context/adapters/ecc/memory_reader.py +162 -0
  29. package/src/harness_context/adapters/ecc/provenance.py +16 -0
  30. package/src/harness_context/api/__init__.py +1 -0
  31. package/src/harness_context/api/v2/__init__.py +12 -0
  32. package/src/harness_context/api/v2/contracts.py +119 -0
  33. package/src/harness_context/api/v2/diagnostics.py +13 -0
  34. package/src/harness_context/api/v2/enums.py +17 -0
  35. package/src/harness_context/api/v2/errors.py +32 -0
  36. package/src/harness_context/api/v2/models.py +4 -0
  37. package/src/harness_context/api/v2/requests.py +17 -0
  38. package/src/harness_context/api/v2/responses.py +22 -0
  39. package/src/harness_context/application/__init__.py +3 -0
  40. package/src/harness_context/application/container.py +31 -0
  41. package/src/harness_context/application/context_service.py +51 -0
  42. package/src/harness_context/application/ecc_service.py +7 -0
  43. package/src/harness_context/application/handoff_service.py +11 -0
  44. package/src/harness_context/application/memory_service.py +9 -0
  45. package/src/harness_context/application/protocols.py +46 -0
  46. package/src/harness_context/application/refresh_service.py +25 -0
  47. package/src/harness_context/application/retrieval_service.py +22 -0
  48. package/src/harness_context/application/services.py +4 -0
  49. package/src/harness_context/application/workspace_service.py +18 -0
  50. package/src/harness_context/bootstrap.py +47 -0
  51. package/src/harness_context/branding.py +16 -0
  52. package/src/harness_context/cli/__init__.py +1 -0
  53. package/src/harness_context/cli/app.py +239 -0
  54. package/src/harness_context/cli/exit_codes.py +25 -0
  55. package/src/harness_context/domain/__init__.py +9 -0
  56. package/src/harness_context/domain/cag.py +18 -0
  57. package/src/harness_context/domain/chunking.py +17 -0
  58. package/src/harness_context/domain/planning.py +30 -0
  59. package/src/harness_context/domain/ports.py +24 -0
  60. package/src/harness_context/domain/retrieval.py +46 -0
  61. package/src/harness_context/engine.py +10 -0
  62. package/src/harness_context/free_tools.py +143 -0
  63. package/src/harness_context/infrastructure/__init__.py +10 -0
  64. package/src/harness_context/infrastructure/graph.py +26 -0
  65. package/src/harness_context/infrastructure/indexes.py +33 -0
  66. package/src/harness_context/infrastructure/local_engine.py +296 -0
  67. package/src/harness_context/infrastructure/parsing.py +38 -0
  68. package/src/harness_context/infrastructure/scanning.py +51 -0
  69. package/src/harness_context/installer/__init__.py +4 -0
  70. package/src/harness_context/installer/models.py +22 -0
  71. package/src/harness_context/installer/service.py +168 -0
  72. package/src/harness_context/mcp/__init__.py +3 -0
  73. package/src/harness_context/mcp/capabilities.py +11 -0
  74. package/src/harness_context/mcp/errors.py +8 -0
  75. package/src/harness_context/mcp/lifecycle.py +72 -0
  76. package/src/harness_context/mcp/middleware.py +57 -0
  77. package/src/harness_context/mcp/server.py +3 -0
  78. package/src/harness_context/mcp/tool_handlers/__init__.py +7 -0
  79. package/src/harness_context/mcp/tool_handlers/context.py +16 -0
  80. package/src/harness_context/mcp/tool_handlers/ecc.py +8 -0
  81. package/src/harness_context/mcp/tool_handlers/handoffs.py +20 -0
  82. package/src/harness_context/mcp/tool_handlers/memory.py +16 -0
  83. package/src/harness_context/mcp/tool_handlers/workspace.py +12 -0
  84. package/src/harness_context/mcp/tools.py +15 -0
  85. package/src/harness_context/observability/__init__.py +6 -0
  86. package/src/harness_context/observability/events.py +25 -0
  87. package/src/harness_context/observability/metrics.py +20 -0
  88. package/src/harness_context/paths.py +35 -0
  89. package/src/harness_context/runtime.py +127 -0
  90. package/src/harness_context/schemas.py +38 -0
  91. package/src/harness_context/security/__init__.py +3 -0
  92. package/src/harness_context/security/secret_patterns.py +15 -0
  93. package/src/harness_context/server.py +1077 -0
  94. package/src/harness_context/storage/__init__.py +6 -0
  95. package/src/harness_context/storage/migrations.py +24 -0
  96. package/src/harness_context/storage/pins.py +10 -0
  97. package/src/harness_context/storage/snapshots.py +149 -0
  98. package/src/harness_context/tokenize.py +12 -0
  99. package/src/harness_context/topology.py +65 -0
  100. package/src/harness_context/watcher/__init__.py +3 -0
  101. package/src/harness_context/watcher/service.py +32 -0
  102. package/src/harness_context/workspace/__init__.py +13 -0
  103. package/src/harness_context/workspace/identity.py +9 -0
  104. package/src/harness_context/workspace/lock.py +24 -0
  105. package/src/harness_context/workspace/policy.py +3 -0
  106. package/src/harness_context/workspace/roots.py +84 -0
  107. package/src/harness_context/workspace/state.py +35 -0
  108. package/src/memory/episodic.py +257 -0
  109. package/src/memory/vector_store.py +104 -0
  110. package/src/retrieval/bm25.py +23 -0
  111. package/src/retrieval/cache.py +76 -0
  112. package/src/retrieval/embeddings.py +75 -0
  113. package/src/retrieval/graph.py +45 -0
  114. package/src/retrieval/reranker.py +78 -0
  115. package/src/retrieval/tokenize.py +11 -0
@@ -0,0 +1,127 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ try:
8
+ import tomllib
9
+ except ModuleNotFoundError: # Python 3.10
10
+ import tomli as tomllib
11
+
12
+ from harness_context.application.container import ApplicationContainer
13
+ from harness_context.bootstrap import build_container, workspace_identity
14
+ from harness_context.mcp.lifecycle import ServerLifecycle
15
+ from harness_context.paths import legacy_workspace_data_dir, workspace_data_dir
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class RuntimeConfig:
20
+ workspace: Path
21
+ workspace_id: str
22
+ transport: str = "stdio"
23
+ host: str = "127.0.0.1"
24
+ port: int = 8765
25
+ startup_policy: str = "block_until_ready"
26
+ watch: bool = False
27
+ ecc_enabled: bool = False
28
+ ecc_allow_user_scope: bool = False
29
+
30
+
31
+ class HarnessRuntime:
32
+ def __init__(self, config: RuntimeConfig):
33
+ self.config = config
34
+ self.container: ApplicationContainer | None = None
35
+ self.lifecycle = ServerLifecycle()
36
+
37
+ @classmethod
38
+ def for_workspace(
39
+ cls, workspace: str, transport: str | None = None,
40
+ host: str | None = None, port: int | None = None, watch: bool | None = None,
41
+ ecc_enabled: bool | None = None, ecc_allow_user_scope: bool | None = None,
42
+ ) -> HarnessRuntime:
43
+ root = Path(workspace).expanduser().resolve(strict=True)
44
+ values = {"transport": "stdio", "host": "127.0.0.1", "port": 8765, "startup_policy": "block_until_ready", "watch": False, "ecc_enabled": False, "ecc_allow_user_scope": False}
45
+ for path in (
46
+ Path.home() / ".config" / "harness" / "config.toml",
47
+ Path.home() / ".config" / "ctxora" / "config.toml",
48
+ legacy_workspace_data_dir(root) / "config.toml",
49
+ workspace_data_dir(root) / "config.toml",
50
+ ):
51
+ if path.exists():
52
+ loaded = tomllib.loads(path.read_text("utf-8"))
53
+ unknown = set(loaded) - set(values)
54
+ if unknown:
55
+ raise ValueError(f"unknown runtime config keys: {sorted(unknown)}")
56
+ values.update(loaded)
57
+ def environment_value(name: str) -> str | None:
58
+ return os.environ.get(f"CTXORA_{name}") or os.environ.get(f"HARNESS_{name}")
59
+
60
+ environment = {
61
+ "transport": environment_value("TRANSPORT"),
62
+ "host": environment_value("HOST"),
63
+ "port": environment_value("PORT"),
64
+ "ecc_enabled": environment_value("ECC_ENABLED"),
65
+ "ecc_allow_user_scope": environment_value("ECC_ALLOW_USER_SCOPE"),
66
+ }
67
+ values.update({key: value for key, value in environment.items() if value not in {None, ""}})
68
+ overrides = {"transport": transport, "host": host, "port": port, "watch": watch, "ecc_enabled": ecc_enabled, "ecc_allow_user_scope": ecc_allow_user_scope}
69
+ values.update({key: value for key, value in overrides.items() if value is not None})
70
+ def boolean(value) -> bool:
71
+ return value.casefold() in {"1", "true", "yes", "on"} if isinstance(value, str) else bool(value)
72
+ user_scope = boolean(values["ecc_allow_user_scope"])
73
+ return cls(RuntimeConfig(
74
+ root, workspace_identity(root), str(values["transport"]), str(values["host"]),
75
+ int(values["port"]), str(values["startup_policy"]), boolean(values["watch"]),
76
+ boolean(values["ecc_enabled"]) or user_scope, user_scope,
77
+ ))
78
+
79
+ def startup(self) -> dict:
80
+ self.container = build_container(
81
+ self.config.workspace, self.config.workspace_id,
82
+ ecc_enabled=self.config.ecc_enabled,
83
+ ecc_allow_user_scope=self.config.ecc_allow_user_scope,
84
+ )
85
+ warm = self.container.retrieval.snapshot_is_current(self.config.workspace_id)
86
+ if not warm:
87
+ refresh = self.container.refresh.execute(self.config.workspace_id)
88
+ else:
89
+ refresh = self.container.retrieval.stats(self.config.workspace_id)
90
+ self.lifecycle.mark_ready()
91
+ return {"warm_start": warm, **refresh}
92
+
93
+ def health_report(self) -> dict[str, object]:
94
+ lifecycle = self.lifecycle.status()
95
+ stats = self.container.retrieval.stats(self.config.workspace_id) if self.container else {}
96
+ ready = bool(lifecycle["ready"] and stats.get("status") == "ready")
97
+ return {
98
+ "schema_version": 1, "status": "ready" if ready else "not_ready",
99
+ "ready": ready, "draining": lifecycle["draining"],
100
+ "active_requests": lifecycle["active_requests"],
101
+ "snapshot_version": int(stats.get("snapshot_version", 0)),
102
+ "files": int(stats.get("files", 0)), "chunks": int(stats.get("chunks", 0)),
103
+ }
104
+
105
+ def drain(self, timeout: float = 10.0) -> bool:
106
+ return self.lifecycle.drain(timeout)
107
+
108
+ def run(self) -> None:
109
+ if self.container is None:
110
+ self.startup()
111
+ from harness_context.mcp.server import create_mcp_server
112
+ from harness_context.watcher import WorkspaceWatcher
113
+ server = create_mcp_server(self.container, self.config.workspace_id, self.lifecycle)
114
+ watcher = WorkspaceWatcher(self.container.retrieval, self.container.refresh, self.config.workspace_id) if self.config.watch else None
115
+ if watcher:
116
+ watcher.start()
117
+ try:
118
+ if self.config.transport == "streamable-http":
119
+ server.settings.host = self.config.host
120
+ server.settings.port = self.config.port
121
+ server.run(transport="streamable-http")
122
+ return
123
+ server.run(transport="stdio")
124
+ finally:
125
+ self.drain()
126
+ if watcher:
127
+ watcher.stop()
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass, field
4
+ from typing import Any
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class WorkspacePolicy:
9
+ roots: tuple[str, ...]
10
+ max_files: int = 10_000
11
+ max_file_bytes: int = 2_000_000
12
+ max_total_bytes: int = 200_000_000
13
+
14
+
15
+ @dataclass
16
+ class ContextItem:
17
+ chunk_id: str
18
+ workspace_id: str
19
+ path: str
20
+ start_line: int
21
+ end_line: int
22
+ type: str
23
+ symbol: str
24
+ content: str
25
+ tokens: int
26
+ content_hash: str
27
+ score: float = 0.0
28
+ signals: dict[str, float] = field(default_factory=dict)
29
+ provenance: str = "workspace"
30
+
31
+ def to_dict(self) -> dict[str, Any]:
32
+ return asdict(self)
33
+
34
+
35
+ class HarnessError(ValueError):
36
+ def __init__(self, code: str, message: str):
37
+ super().__init__(message)
38
+ self.code = code
@@ -0,0 +1,3 @@
1
+ from harness_context.security.secret_patterns import contains_secret
2
+
3
+ __all__ = ["contains_secret"]
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+
5
+ _PATTERNS = (
6
+ re.compile(rb"\bsk-[A-Za-z0-9_-]{16,}\b", re.IGNORECASE),
7
+ re.compile(rb"\b(?:sk|rk)_live_[A-Za-z0-9]{16,}\b"),
8
+ re.compile(rb"\b(?:gh[pors]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{16,})\b"),
9
+ re.compile(rb"\b(?:AKIA|ASIA)[A-Z0-9]{16}\b"),
10
+ re.compile(rb"-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----"),
11
+ )
12
+
13
+
14
+ def contains_secret(content: bytes) -> bool:
15
+ return any(pattern.search(content) for pattern in _PATTERNS)