thread-archive 0.0.2__py3-none-any.whl

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 (132) hide show
  1. thread_archive/__init__.py +34 -0
  2. thread_archive/_api.py +2235 -0
  3. thread_archive/_config.py +126 -0
  4. thread_archive/_importers/__init__.py +81 -0
  5. thread_archive/_importers/_continuation.py +136 -0
  6. thread_archive/_importers/_cursor.py +103 -0
  7. thread_archive/_importers/_events.py +244 -0
  8. thread_archive/_importers/_line_stream.py +193 -0
  9. thread_archive/_importers/_read.py +82 -0
  10. thread_archive/_importers/_result.py +17 -0
  11. thread_archive/_importers/_sidecar.py +113 -0
  12. thread_archive/_importers/_state.py +240 -0
  13. thread_archive/_importers/_titles.py +179 -0
  14. thread_archive/_importers/antigravity.py +254 -0
  15. thread_archive/_importers/claude_code.py +293 -0
  16. thread_archive/_importers/claude_science.py +330 -0
  17. thread_archive/_importers/cloth.py +20 -0
  18. thread_archive/_importers/codex.py +324 -0
  19. thread_archive/_importers/cowork.py +107 -0
  20. thread_archive/_importers/cursor.py +432 -0
  21. thread_archive/_importers/exports.py +389 -0
  22. thread_archive/_importers/grok.py +600 -0
  23. thread_archive/_importers/opencode.py +538 -0
  24. thread_archive/_knowledge/__init__.py +67 -0
  25. thread_archive/_knowledge/_claims.py +116 -0
  26. thread_archive/_knowledge/_community.py +75 -0
  27. thread_archive/_knowledge/graph.py +208 -0
  28. thread_archive/_knowledge/materialize.py +212 -0
  29. thread_archive/_knowledge/write.py +438 -0
  30. thread_archive/_launchd.py +167 -0
  31. thread_archive/_mcp/__init__.py +1 -0
  32. thread_archive/_mcp/librarian.py +151 -0
  33. thread_archive/_mcp/server.py +307 -0
  34. thread_archive/_retrieval/__init__.py +280 -0
  35. thread_archive/_retrieval/_classify.py +80 -0
  36. thread_archive/_retrieval/_codex.py +157 -0
  37. thread_archive/_retrieval/_context.py +123 -0
  38. thread_archive/_retrieval/_extract.py +184 -0
  39. thread_archive/_retrieval/embed.py +186 -0
  40. thread_archive/_retrieval/format.py +149 -0
  41. thread_archive/_retrieval/fts.py +538 -0
  42. thread_archive/_retrieval/rank.py +228 -0
  43. thread_archive/_retrieval/read.py +908 -0
  44. thread_archive/_retrieval/rerank.py +143 -0
  45. thread_archive/_retrieval/vectors.py +611 -0
  46. thread_archive/_scripts/__init__.py +1 -0
  47. thread_archive/_scripts/backfill_recompute.py +328 -0
  48. thread_archive/_scripts/backfill_reconcile.py +296 -0
  49. thread_archive/_scripts/denamespace_dedup_keys.py +120 -0
  50. thread_archive/_scripts/recover_dropped_events.py +190 -0
  51. thread_archive/_scripts/repair_grok_tool_names.py +118 -0
  52. thread_archive/_scripts/repair_grok_tool_names_backup_20260704T155625Z.json +275 -0
  53. thread_archive/_scripts/repair_grok_tool_names_plan_20260704.json +435 -0
  54. thread_archive/_setup/__init__.py +12 -0
  55. thread_archive/_setup/clients.py +89 -0
  56. thread_archive/_setup/wizard.py +474 -0
  57. thread_archive/_store/__init__.py +45 -0
  58. thread_archive/_store/_base.py +173 -0
  59. thread_archive/_store/_defaults.py +57 -0
  60. thread_archive/_store/_types.py +38 -0
  61. thread_archive/_store/models.py +370 -0
  62. thread_archive/_store/schema.py +84 -0
  63. thread_archive/_thread_import/__init__.py +40 -0
  64. thread_archive/_thread_import/api.py +78 -0
  65. thread_archive/_thread_import/event_builder.py +810 -0
  66. thread_archive/_thread_import/exporters/__init__.py +9 -0
  67. thread_archive/_thread_import/exporters/_cursor_kv_mixin.py +166 -0
  68. thread_archive/_thread_import/exporters/_cursor_parse_mixin.py +149 -0
  69. thread_archive/_thread_import/exporters/cursor.py +582 -0
  70. thread_archive/_thread_import/exporters/cursor_parse.py +145 -0
  71. thread_archive/_thread_import/parsers/__init__.py +191 -0
  72. thread_archive/_thread_import/parsers/base.py +698 -0
  73. thread_archive/_thread_import/parsers/chatgpt.py +722 -0
  74. thread_archive/_thread_import/parsers/chatgpt_content.py +332 -0
  75. thread_archive/_thread_import/parsers/claude.py +443 -0
  76. thread_archive/_thread_import/parsers/claude_code.py +1035 -0
  77. thread_archive/_thread_import/parsers/claude_code_blocks.py +415 -0
  78. thread_archive/_thread_import/parsers/claude_code_ide.py +122 -0
  79. thread_archive/_thread_import/parsers/claude_code_sessions.py +90 -0
  80. thread_archive/_thread_import/parsers/config/__init__.py +26 -0
  81. thread_archive/_thread_import/parsers/config/base.py +220 -0
  82. thread_archive/_thread_import/parsers/cursor.py +538 -0
  83. thread_archive/_thread_import/parsers/cursor_blocks.py +365 -0
  84. thread_archive/_thread_import/parsers/pipeline/__init__.py +29 -0
  85. thread_archive/_thread_import/parsers/pipeline/base.py +251 -0
  86. thread_archive/_thread_import/parsers/pipeline/interfaces.py +191 -0
  87. thread_archive/_thread_import/parsers/transformers/__init__.py +22 -0
  88. thread_archive/_thread_import/parsers/transformers/active_path.py +87 -0
  89. thread_archive/_thread_import/parsers/transformers/coalescing.py +389 -0
  90. thread_archive/_thread_import/parsers/transformers/ide_context.py +158 -0
  91. thread_archive/_thread_import/parsers/transformers/thinking_merge.py +68 -0
  92. thread_archive/_thread_import/parsers/types/__init__.py +56 -0
  93. thread_archive/_thread_import/parsers/types/chatgpt.py +99 -0
  94. thread_archive/_thread_import/parsers/types/claude.py +120 -0
  95. thread_archive/_thread_import/parsers/types/claude_code.py +139 -0
  96. thread_archive/_thread_import/parsers/types/cursor.py +109 -0
  97. thread_archive/_thread_import/parsers/validators/__init__.py +83 -0
  98. thread_archive/_thread_import/parsers/validators/base.py +168 -0
  99. thread_archive/_thread_import/parsers/validators/content.py +80 -0
  100. thread_archive/_thread_import/parsers/validators/referential.py +57 -0
  101. thread_archive/_thread_import/parsers/validators/thinking.py +143 -0
  102. thread_archive/_thread_import/parsers/validators/types.py +87 -0
  103. thread_archive/_thread_import/schemas/__init__.py +231 -0
  104. thread_archive/_thread_import/schemas/chatgpt/v1.json +197 -0
  105. thread_archive/_thread_import/schemas/chatgpt/v2.json +203 -0
  106. thread_archive/_thread_import/schemas/claude/v1.json +188 -0
  107. thread_archive/_thread_import/schemas/claude_code/v1.json +183 -0
  108. thread_archive/_thread_import/schemas/cursor/v1.json +143 -0
  109. thread_archive/_thread_import/schemas/cursor/v2.json +200 -0
  110. thread_archive/_thread_import/timestamps.py +57 -0
  111. thread_archive/_thread_import/tool_names.py +48 -0
  112. thread_archive/_truth/__init__.py +40 -0
  113. thread_archive/_truth/jsonl_log.py +2340 -0
  114. thread_archive/_truth/repair.py +322 -0
  115. thread_archive/_watcher/__init__.py +57 -0
  116. thread_archive/_watcher/base.py +65 -0
  117. thread_archive/_watcher/daemon.py +289 -0
  118. thread_archive/_watcher/export_drop.py +203 -0
  119. thread_archive/_watcher/exthost.py +316 -0
  120. thread_archive/_watcher/lazy.py +117 -0
  121. thread_archive/_watcher/sources.py +616 -0
  122. thread_archive/_web/__init__.py +15 -0
  123. thread_archive/_web/server.py +299 -0
  124. thread_archive/_web/static/assets/index-DECSH1Mz.css +10 -0
  125. thread_archive/_web/static/assets/index-Djq0FK0y.js +101 -0
  126. thread_archive/_web/static/index.html +13 -0
  127. thread_archive/cli.py +746 -0
  128. thread_archive-0.0.2.dist-info/METADATA +296 -0
  129. thread_archive-0.0.2.dist-info/RECORD +132 -0
  130. thread_archive-0.0.2.dist-info/WHEEL +4 -0
  131. thread_archive-0.0.2.dist-info/entry_points.txt +6 -0
  132. thread_archive-0.0.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,220 @@
1
+ """
2
+ Provider configuration dataclasses.
3
+
4
+ ProviderConfig captures provider-specific characteristics that affect
5
+ parsing and validation. This moves configuration out of base.py and
6
+ makes it owned by each provider.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Dict, Literal, Optional, Set
11
+
12
+ # Type alias for thinking block expectations
13
+ ThinkingExpectation = Literal["never", "required", "model_specific", "optional"]
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class ProviderConfig:
18
+ """Provider-specific parsing and validation configuration.
19
+
20
+ Each parser has a PROVIDER_CONFIG class attribute that configures
21
+ validation behavior, expected roles/block types, and model-specific
22
+ thinking requirements.
23
+
24
+ Attributes:
25
+ provider_name: Unique identifier for the provider
26
+ thinking_expectation: How to validate thinking blocks
27
+ - "never": Error if thinking blocks exist (wrong parser/source)
28
+ - "required": Error if no thinking blocks (incomplete export)
29
+ - "model_specific": Validate per-message based on model field
30
+ - "optional": No validation
31
+ thinking_exempt_models: Models that don't require thinking blocks
32
+ (only used when thinking_expectation="model_specific")
33
+ has_branching: Whether conversations can have branches/alternate paths
34
+ has_parent_references: Whether messages have parent references
35
+ expected_roles: Valid role values for this provider
36
+ expected_block_types: Valid content block types for this provider
37
+ timestamp_format: How timestamps are formatted in exports
38
+ - "unix_seconds": Unix timestamp in seconds
39
+ - "unix_millis": Unix timestamp in milliseconds
40
+ - "iso": ISO 8601 string
41
+ - "mixed": Can be any of the above (auto-detect)
42
+ """
43
+
44
+ provider_name: str
45
+ thinking_expectation: ThinkingExpectation = "optional"
46
+ thinking_exempt_models: Set[str] = field(default_factory=set)
47
+ has_branching: bool = False
48
+ has_parent_references: bool = False
49
+ expected_roles: Set[str] = field(
50
+ default_factory=lambda: {"user", "assistant", "system"}
51
+ )
52
+ expected_block_types: Set[str] = field(default_factory=set)
53
+ timestamp_format: Literal["unix_seconds", "unix_millis", "iso", "mixed"] = "mixed"
54
+
55
+ def requires_thinking(self, model: Optional[str]) -> bool:
56
+ """Check if a specific model requires thinking blocks.
57
+
58
+ Default behavior: unknown models REQUIRE thinking (safer - catches missing data).
59
+ Only whitelisted non-reasoning models are exempt.
60
+
61
+ Args:
62
+ model: Model name/slug from the message
63
+
64
+ Returns:
65
+ True if thinking blocks are required for this model
66
+ """
67
+ if self.thinking_expectation != "model_specific":
68
+ return self.thinking_expectation == "required"
69
+
70
+ if not model:
71
+ return True # Unknown model = require thinking
72
+
73
+ model_lower = model.lower()
74
+
75
+ # Check if model is in the exempt list
76
+ for exempt_model in self.thinking_exempt_models:
77
+ exempt_lower = exempt_model.lower()
78
+ if model_lower == exempt_lower or model_lower.startswith(f"{exempt_lower}-"):
79
+ return False # Known non-reasoning model
80
+
81
+ return True # Default: require thinking
82
+
83
+
84
+ # =============================================================================
85
+ # Provider-specific configurations
86
+ # =============================================================================
87
+
88
+ # Common non-reasoning models shared across providers
89
+ _COMMON_EXEMPT_MODELS = {
90
+ # OpenAI non-reasoning models
91
+ "gpt-4",
92
+ "gpt-4-turbo",
93
+ "gpt-4o",
94
+ "gpt-4o-mini",
95
+ "gpt-3.5-turbo",
96
+ "gpt-3.5",
97
+ "chatgpt-4o-latest",
98
+ # Anthropic non-thinking models (note: Opus 4.5 DOES have thinking)
99
+ "claude-3-5-sonnet",
100
+ "claude-sonnet-4-5",
101
+ "claude-3-5-sonnet-20241022",
102
+ "claude-3-5-haiku",
103
+ "claude-haiku-3-5",
104
+ "claude-3-opus",
105
+ "claude-3-sonnet",
106
+ "claude-3-haiku",
107
+ "claude-2",
108
+ "claude-2.1",
109
+ "claude-instant",
110
+ "claude-sonnet-4-5-20250929",
111
+ }
112
+
113
+
114
+ CHATGPT_CONFIG = ProviderConfig(
115
+ provider_name="chatgpt",
116
+ thinking_expectation="model_specific",
117
+ thinking_exempt_models=_COMMON_EXEMPT_MODELS,
118
+ has_branching=True,
119
+ has_parent_references=True,
120
+ expected_roles={"user", "assistant", "system", "tool"},
121
+ expected_block_types={
122
+ "text",
123
+ "thinking",
124
+ "tool_use",
125
+ "tool_result",
126
+ "image",
127
+ "system_context",
128
+ },
129
+ timestamp_format="unix_seconds",
130
+ )
131
+
132
+
133
+ CLAUDE_CONFIG = ProviderConfig(
134
+ provider_name="claude",
135
+ # Claude.ai web exports NEVER include thinking blocks (server-side only)
136
+ thinking_expectation="never",
137
+ thinking_exempt_models=set(), # Not applicable - expectation is "never"
138
+ has_branching=False,
139
+ has_parent_references=False,
140
+ expected_roles={"user", "assistant", "human"}, # Claude uses "human" for user
141
+ expected_block_types={
142
+ "text",
143
+ "tool_use",
144
+ "tool_result",
145
+ "system_metadata",
146
+ },
147
+ timestamp_format="iso",
148
+ )
149
+
150
+
151
+ CLAUDE_CODE_CONFIG = ProviderConfig(
152
+ provider_name="claude-code",
153
+ thinking_expectation="model_specific",
154
+ thinking_exempt_models=_COMMON_EXEMPT_MODELS,
155
+ has_branching=True, # Tree via uuid/parentUuid
156
+ has_parent_references=True,
157
+ expected_roles={"user", "assistant", "system"},
158
+ expected_block_types={
159
+ "text",
160
+ "thinking",
161
+ "tool_use",
162
+ "tool_result",
163
+ "ide_context",
164
+ "file_snapshot",
165
+ "context_summary",
166
+ "system_context",
167
+ "parse_error",
168
+ "image", # pasted screenshots/images in a Claude Code session
169
+ "queue_operation", # Claude Code's message-queue feature (queued while the agent works)
170
+ "progress", # hook/tool progress telemetry (e.g. PostToolUse hook callbacks)
171
+ },
172
+ timestamp_format="iso",
173
+ )
174
+
175
+
176
+ CURSOR_CONFIG = ProviderConfig(
177
+ provider_name="cursor",
178
+ thinking_expectation="model_specific",
179
+ thinking_exempt_models=_COMMON_EXEMPT_MODELS,
180
+ has_branching=False,
181
+ has_parent_references=False,
182
+ expected_roles={"user", "assistant", "system", "tool"},
183
+ expected_block_types={
184
+ "text",
185
+ "thinking",
186
+ "tool_use",
187
+ "tool_result",
188
+ "code",
189
+ },
190
+ timestamp_format="mixed", # Can be ISO string or Unix timestamp
191
+ )
192
+
193
+
194
+ # Registry of all provider configs
195
+ _PROVIDER_CONFIGS: Dict[str, ProviderConfig] = {
196
+ "chatgpt": CHATGPT_CONFIG,
197
+ "claude": CLAUDE_CONFIG,
198
+ "claude-code": CLAUDE_CODE_CONFIG,
199
+ "cursor": CURSOR_CONFIG,
200
+ }
201
+
202
+
203
+ def get_provider_config(provider: str) -> ProviderConfig:
204
+ """Get the configuration for a provider.
205
+
206
+ Args:
207
+ provider: Provider name (chatgpt, claude, claude-code, cursor)
208
+
209
+ Returns:
210
+ ProviderConfig for the provider
211
+
212
+ Raises:
213
+ KeyError: If provider is not known
214
+ """
215
+ if provider not in _PROVIDER_CONFIGS:
216
+ raise KeyError(
217
+ f"Unknown provider: {provider}. "
218
+ f"Known providers: {list(_PROVIDER_CONFIGS.keys())}"
219
+ )
220
+ return _PROVIDER_CONFIGS[provider]