cc-transcript 14.2.0__tar.gz → 14.4.0__tar.gz
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.
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/PKG-INFO +1 -1
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/__init__.py +18 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/__init__.pyi +35 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/_native.pyi +52 -9
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/spec.py +23 -8
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/parser.py +30 -0
- cc_transcript-14.4.0/cc_transcript/synthetic.py +219 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/tools.py +27 -8
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/pyproject.toml +1 -1
- cc_transcript-14.4.0/rust/crates/core/src/codex/mod.rs +10 -0
- cc_transcript-14.4.0/rust/crates/core/src/codex/parse.rs +1270 -0
- cc_transcript-14.4.0/rust/crates/core/src/codex/protocol.rs +129 -0
- cc_transcript-14.4.0/rust/crates/core/src/codex/types.rs +260 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/lib.rs +1 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/toolcall.rs +227 -18
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/types.rs +4 -4
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/mining.rs +3 -3
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/toolcall.rs +1 -8
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/toolcall.rs +110 -11
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/Cargo.lock +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/Cargo.toml +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/LICENSE +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/README.md +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/activity.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/command.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/context.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/corrections.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/cost.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/decisions.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/evidence.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/facts.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/heartbeats.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/ids.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/ledger.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/literals.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/models.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/nlp.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/notifications.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/query.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/render.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/cc_transcript/watch.py +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/Cargo.toml +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/corrections.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/digest.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/facts.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/grep.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/list.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/mod.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/show.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/slice.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/stats.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/commands/watch.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/lib.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/main.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/output.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/cli/src/target.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/Cargo.toml +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/benches/parse.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/benches/render.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/benches/toolcall.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/activity.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/buckets.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/command.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/context.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/corrections.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/cost.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/discovery.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/facts.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/feedback.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/filter.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/ids.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/literals/command.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/literals/corrections.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/literals/feedback.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/literals/mining.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/literals/mod.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/literals/protocol.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/notifications.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/parse.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/protocol.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/pystr.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/query.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/render.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/sqlite.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/value.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/core/src/watch.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/Cargo.toml +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/build.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/data/command_prefix_pins.tsv +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/bin/stub_gen.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/command.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/context.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/corrections.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/discovery.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/feedback.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/lexicon.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/lib.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/nlp.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/python.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/render.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/score.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/sqlite.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/attachment.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/blocks.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/command.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/convert.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/dunder.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/events.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/meta.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/mod.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/print.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/store.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/system.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/toolresult.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/views/transcript.rs +0 -0
- {cc_transcript-14.2.0 → cc_transcript-14.4.0}/rust/crates/py/src/watch.rs +0 -0
|
@@ -53,6 +53,7 @@ EXPORTS: dict[str, str] = {
|
|
|
53
53
|
"ReadResult",
|
|
54
54
|
"SkillCall",
|
|
55
55
|
"SkillResult",
|
|
56
|
+
"SpanEditCall",
|
|
56
57
|
"TaskCall",
|
|
57
58
|
"TaskCreateCall",
|
|
58
59
|
"TaskLaunchResult",
|
|
@@ -76,7 +77,9 @@ EXPORTS: dict[str, str] = {
|
|
|
76
77
|
"mcp_parts",
|
|
77
78
|
"parse_tool_call",
|
|
78
79
|
"parse_tool_result",
|
|
80
|
+
"register_mcp_tool",
|
|
79
81
|
"tool_name_matches",
|
|
82
|
+
"unregister_mcp_tool",
|
|
80
83
|
),
|
|
81
84
|
"cc_transcript.command": (
|
|
82
85
|
"Command",
|
|
@@ -142,10 +145,25 @@ EXPORTS: dict[str, str] = {
|
|
|
142
145
|
"cc_transcript.parser": (
|
|
143
146
|
"parse",
|
|
144
147
|
"parse_event",
|
|
148
|
+
"parse_events",
|
|
145
149
|
"parse_events_from_bytes",
|
|
146
150
|
"parse_print_result",
|
|
147
151
|
"stream",
|
|
148
152
|
),
|
|
153
|
+
"cc_transcript.synthetic": (
|
|
154
|
+
"assistant_line",
|
|
155
|
+
"meta_fields",
|
|
156
|
+
"mode_line",
|
|
157
|
+
"other_line",
|
|
158
|
+
"synthetic_assistant_event",
|
|
159
|
+
"synthetic_user_event",
|
|
160
|
+
"system_line",
|
|
161
|
+
"text_block",
|
|
162
|
+
"thinking_block",
|
|
163
|
+
"tool_result",
|
|
164
|
+
"tool_use",
|
|
165
|
+
"user_line",
|
|
166
|
+
),
|
|
149
167
|
"cc_transcript.cost": (
|
|
150
168
|
"PRICING",
|
|
151
169
|
"CostBreakdown",
|
|
@@ -187,6 +187,7 @@ from cc_transcript.notifications import (
|
|
|
187
187
|
from cc_transcript.parser import (
|
|
188
188
|
parse as parse,
|
|
189
189
|
parse_event as parse_event,
|
|
190
|
+
parse_events as parse_events,
|
|
190
191
|
parse_events_from_bytes as parse_events_from_bytes,
|
|
191
192
|
parse_print_result as parse_print_result,
|
|
192
193
|
stream as stream,
|
|
@@ -207,6 +208,21 @@ from cc_transcript.render import (
|
|
|
207
208
|
render_turn as render_turn,
|
|
208
209
|
)
|
|
209
210
|
|
|
211
|
+
from cc_transcript.synthetic import (
|
|
212
|
+
assistant_line as assistant_line,
|
|
213
|
+
meta_fields as meta_fields,
|
|
214
|
+
mode_line as mode_line,
|
|
215
|
+
other_line as other_line,
|
|
216
|
+
synthetic_assistant_event as synthetic_assistant_event,
|
|
217
|
+
synthetic_user_event as synthetic_user_event,
|
|
218
|
+
system_line as system_line,
|
|
219
|
+
text_block as text_block,
|
|
220
|
+
thinking_block as thinking_block,
|
|
221
|
+
tool_result as tool_result,
|
|
222
|
+
tool_use as tool_use,
|
|
223
|
+
user_line as user_line,
|
|
224
|
+
)
|
|
225
|
+
|
|
210
226
|
from cc_transcript.tools import (
|
|
211
227
|
AskUserQuestionResult as AskUserQuestionResult,
|
|
212
228
|
BashCall as BashCall,
|
|
@@ -227,6 +243,7 @@ from cc_transcript.tools import (
|
|
|
227
243
|
ReadResult as ReadResult,
|
|
228
244
|
SkillCall as SkillCall,
|
|
229
245
|
SkillResult as SkillResult,
|
|
246
|
+
SpanEditCall as SpanEditCall,
|
|
230
247
|
TOOL_ALIASES as TOOL_ALIASES,
|
|
231
248
|
TaskCall as TaskCall,
|
|
232
249
|
TaskCreateCall as TaskCreateCall,
|
|
@@ -251,7 +268,9 @@ from cc_transcript.tools import (
|
|
|
251
268
|
mcp_parts as mcp_parts,
|
|
252
269
|
parse_tool_call as parse_tool_call,
|
|
253
270
|
parse_tool_result as parse_tool_result,
|
|
271
|
+
register_mcp_tool as register_mcp_tool,
|
|
254
272
|
tool_name_matches as tool_name_matches,
|
|
273
|
+
unregister_mcp_tool as unregister_mcp_tool,
|
|
255
274
|
)
|
|
256
275
|
|
|
257
276
|
from cc_transcript.watch import (
|
|
@@ -360,6 +379,7 @@ __all__ = [
|
|
|
360
379
|
"SessionId",
|
|
361
380
|
"SkillCall",
|
|
362
381
|
"SkillResult",
|
|
382
|
+
"SpanEditCall",
|
|
363
383
|
"StopHookSummary",
|
|
364
384
|
"SubagentIndex",
|
|
365
385
|
"SubagentSession",
|
|
@@ -406,6 +426,7 @@ __all__ = [
|
|
|
406
426
|
"WriteResult",
|
|
407
427
|
"annotate_spec",
|
|
408
428
|
"apply_spec",
|
|
429
|
+
"assistant_line",
|
|
409
430
|
"build_spec",
|
|
410
431
|
"canonical_json",
|
|
411
432
|
"capture_window",
|
|
@@ -440,15 +461,20 @@ __all__ = [
|
|
|
440
461
|
"mcp_access",
|
|
441
462
|
"mcp_parts",
|
|
442
463
|
"mcp_summary",
|
|
464
|
+
"meta_fields",
|
|
465
|
+
"mode_line",
|
|
443
466
|
"native_user_classifier",
|
|
467
|
+
"other_line",
|
|
444
468
|
"parse",
|
|
445
469
|
"parse_command_line",
|
|
446
470
|
"parse_event",
|
|
471
|
+
"parse_events",
|
|
447
472
|
"parse_events_from_bytes",
|
|
448
473
|
"parse_print_result",
|
|
449
474
|
"parse_tool_call",
|
|
450
475
|
"parse_tool_result",
|
|
451
476
|
"record_harvest",
|
|
477
|
+
"register_mcp_tool",
|
|
452
478
|
"render_session",
|
|
453
479
|
"render_tool_call",
|
|
454
480
|
"render_turn",
|
|
@@ -457,9 +483,18 @@ __all__ = [
|
|
|
457
483
|
"result_index",
|
|
458
484
|
"stream",
|
|
459
485
|
"subagent_paths",
|
|
486
|
+
"synthetic_assistant_event",
|
|
487
|
+
"synthetic_user_event",
|
|
488
|
+
"system_line",
|
|
489
|
+
"text_block",
|
|
490
|
+
"thinking_block",
|
|
460
491
|
"thinking_chars",
|
|
461
492
|
"tool_digest",
|
|
462
493
|
"tool_facts",
|
|
463
494
|
"tool_name_matches",
|
|
495
|
+
"tool_result",
|
|
496
|
+
"tool_use",
|
|
464
497
|
"tool_uses",
|
|
498
|
+
"unregister_mcp_tool",
|
|
499
|
+
"user_line",
|
|
465
500
|
]
|
|
@@ -72,6 +72,7 @@ __all__ = [
|
|
|
72
72
|
"ServerToolUse",
|
|
73
73
|
"SkillCall",
|
|
74
74
|
"SkillResult",
|
|
75
|
+
"SpanEditCall",
|
|
75
76
|
"SpecMatcher",
|
|
76
77
|
"StopHookSummary",
|
|
77
78
|
"SystemEvent",
|
|
@@ -133,6 +134,7 @@ __all__ = [
|
|
|
133
134
|
"parse_bytes",
|
|
134
135
|
"parse_print_result",
|
|
135
136
|
"query_session",
|
|
137
|
+
"register_mcp_tool",
|
|
136
138
|
"render_compact_lines",
|
|
137
139
|
"render_haystacks",
|
|
138
140
|
"render_stats",
|
|
@@ -147,6 +149,7 @@ __all__ = [
|
|
|
147
149
|
"tool_name_matches",
|
|
148
150
|
"toolcall_parse_view",
|
|
149
151
|
"toolresult_parse_view",
|
|
152
|
+
"unregister_mcp_tool",
|
|
150
153
|
]
|
|
151
154
|
|
|
152
155
|
@typing.final
|
|
@@ -1131,6 +1134,11 @@ class OtherCall(ToolCallBase):
|
|
|
1131
1134
|
__match_args__: typing.ClassVar[tuple[str, ...]]
|
|
1132
1135
|
@property
|
|
1133
1136
|
def name(self) -> builtins.str: ...
|
|
1137
|
+
@property
|
|
1138
|
+
def error(self) -> typing.Optional[builtins.str]:
|
|
1139
|
+
r"""
|
|
1140
|
+
The strict-parse failure this call degraded from; None when the payload was well-formed but the tool has no typed model.
|
|
1141
|
+
"""
|
|
1134
1142
|
|
|
1135
1143
|
@typing.final
|
|
1136
1144
|
class OtherEvent:
|
|
@@ -1514,6 +1522,27 @@ class SkillResult(ToolResultBase):
|
|
|
1514
1522
|
@property
|
|
1515
1523
|
def allowed_tools(self) -> tuple[str, ...] | None: ...
|
|
1516
1524
|
|
|
1525
|
+
@typing.final
|
|
1526
|
+
class SpanEditCall(ToolCallBase):
|
|
1527
|
+
r"""
|
|
1528
|
+
An in-place file edit addressed by an opaque locator.
|
|
1529
|
+
|
|
1530
|
+
A registered MCP tool whose spec carries a span-edit lowering. The payload
|
|
1531
|
+
carries no pre-image, so :func:`hunks_of` yields no hunk; ``new`` is ``None``
|
|
1532
|
+
when the call deletes rather than writes. Registrations are process-local.
|
|
1533
|
+
|
|
1534
|
+
Attributes:
|
|
1535
|
+
file_path: The file the edit targets.
|
|
1536
|
+
new: The written content, or ``None`` when the call is a deletion.
|
|
1537
|
+
"""
|
|
1538
|
+
__match_args__: typing.ClassVar[tuple[str, ...]]
|
|
1539
|
+
@property
|
|
1540
|
+
def name(self) -> builtins.str: ...
|
|
1541
|
+
@property
|
|
1542
|
+
def file_path(self) -> builtins.str: ...
|
|
1543
|
+
@property
|
|
1544
|
+
def new(self) -> typing.Optional[builtins.str]: ...
|
|
1545
|
+
|
|
1517
1546
|
@typing.final
|
|
1518
1547
|
class SpecMatcher:
|
|
1519
1548
|
def __new__(cls, spec_json: builtins.str) -> SpecMatcher: ...
|
|
@@ -2147,6 +2176,8 @@ def hunks_of(call: tools.ToolCall | tools.FallbackCall) -> tuple[Hunk, ...]:
|
|
|
2147
2176
|
|
|
2148
2177
|
MultiEdit yields one hunk per span in application order — never just the
|
|
2149
2178
|
first. Write and NotebookEdit are pure additions with an empty old side.
|
|
2179
|
+
:class:`~SpanEditCall` yields none: its payload carries no
|
|
2180
|
+
pre-image, so no honest hunk can be derived.
|
|
2150
2181
|
"""
|
|
2151
2182
|
|
|
2152
2183
|
def ids_canonical_json(value_json: builtins.str) -> builtins.str: ...
|
|
@@ -2166,19 +2197,16 @@ def matches_names(actual: builtins.str, names: collections.abc.Container[str]) -
|
|
|
2166
2197
|
Whether ``actual`` is one of ``names``, exactly or as an MCP tool suffix.
|
|
2167
2198
|
|
|
2168
2199
|
True when ``actual`` is in ``names``, or when it splits as
|
|
2169
|
-
``mcp__<server>__<tool>`` on the first two ``__`` and ``<tool>`` — or
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
``
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
taken verbatim; pre-expand with :func:`expand_tool_names` for those.
|
|
2200
|
+
``mcp__<server>__<tool>`` on the first two ``__`` and ``<tool>`` — or the
|
|
2201
|
+
built-in gate its registered spec ``behaves_like`` — is in ``names``. That
|
|
2202
|
+
closure lets a registered MCP tool (e.g. one that gates as ``Edit``) match an
|
|
2203
|
+
``Edit`` gate even under a name no built-in gate would catch. Display-name
|
|
2204
|
+
aliases from :data:`~tools.TOOL_ALIASES` are not closed over —
|
|
2205
|
+
``names`` is taken verbatim; pre-expand with :func:`expand_tool_names` for those.
|
|
2176
2206
|
|
|
2177
2207
|
Example:
|
|
2178
2208
|
>>> matches_names("mcp__github__Grep", {"Grep"})
|
|
2179
2209
|
True
|
|
2180
|
-
>>> matches_names("mcp__cc-context__ccx_code_edit", {"Edit"})
|
|
2181
|
-
True
|
|
2182
2210
|
>>> matches_names("Execute", {"Bash"})
|
|
2183
2211
|
False
|
|
2184
2212
|
"""
|
|
@@ -2230,6 +2258,16 @@ def parse_print_result(raw: bytes) -> PrintResult: ...
|
|
|
2230
2258
|
|
|
2231
2259
|
def query_session(path: builtins.str, max_events: builtins.int) -> dict[str, typing.Any]: ...
|
|
2232
2260
|
|
|
2261
|
+
def register_mcp_tool(tool: builtins.str, behaves_like: builtins.str, span_edit: collections.abc.Mapping[str, str] | None = None) -> None:
|
|
2262
|
+
r"""
|
|
2263
|
+
Register an MCP tool spec, keyed by its bare segment; last write wins.
|
|
2264
|
+
|
|
2265
|
+
``behaves_like`` names the built-in gate the tool matches. ``span_edit``, when
|
|
2266
|
+
given, maps the payload key names — ``path``, ``content``, and optionally
|
|
2267
|
+
``delete`` — the tool's :class:`~SpanEditCall` lowering
|
|
2268
|
+
reads. Registrations are process-local.
|
|
2269
|
+
"""
|
|
2270
|
+
|
|
2233
2271
|
def render_compact_lines(raw: bytes, width: builtins.int, thinking: builtins.bool, uuids: builtins.bool) -> builtins.list[builtins.str]: ...
|
|
2234
2272
|
|
|
2235
2273
|
def render_haystacks(raw: bytes, wheres: typing.Sequence[builtins.str]) -> builtins.list[builtins.str]: ...
|
|
@@ -2275,3 +2313,8 @@ def toolresult_parse_view(name: builtins.str, payload_json: builtins.str) -> too
|
|
|
2275
2313
|
the typed view hierarchy; the ``cc_transcript.tools`` facade owns the public
|
|
2276
2314
|
signature.
|
|
2277
2315
|
"""
|
|
2316
|
+
|
|
2317
|
+
def unregister_mcp_tool(tool: builtins.str) -> builtins.bool:
|
|
2318
|
+
r"""
|
|
2319
|
+
Unregister an MCP tool by its bare segment; returns whether it was registered.
|
|
2320
|
+
"""
|
|
@@ -65,10 +65,23 @@ ALL_DETECTORS: frozenset[DetectorName] = frozenset(
|
|
|
65
65
|
}
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
# Expanded lazily (module __getattr__ and the spec fields' default_factory), so an
|
|
69
|
+
# MCP tool registered after import but before spec build lands in the name sets.
|
|
70
|
+
_TOOL_SETS: dict[str, str] = {
|
|
71
|
+
"EDIT_TOOLS": "Edit|Write|MultiEdit|NotebookEdit",
|
|
72
|
+
"SUBAGENT_TOOLS": "Agent|Task",
|
|
73
|
+
"PLAN_TOOLS": "ExitPlanMode",
|
|
74
|
+
"DENIAL_EXCLUDED_TOOLS": "ExitPlanMode|AskUserQuestion",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def __getattr__(name: str) -> frozenset[str]:
|
|
79
|
+
try:
|
|
80
|
+
return expand_tool_names(_TOOL_SETS[name])
|
|
81
|
+
except KeyError:
|
|
82
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
|
|
83
|
+
|
|
84
|
+
|
|
72
85
|
REENTRY_LOOKBACK = 40
|
|
73
86
|
CONFIDENCE_STEP = 0.25
|
|
74
87
|
SHORT_FOLLOWUP_MAX_WORDS = 2
|
|
@@ -168,7 +181,7 @@ USER_MESSAGE_SPEC = ConfidenceSpec(
|
|
|
168
181
|
class ProvenanceSpec:
|
|
169
182
|
"""Names the subagent tools whose tool results classify as ``claude`` provenance."""
|
|
170
183
|
|
|
171
|
-
subagent_tools: frozenset[str] = SUBAGENT_TOOLS
|
|
184
|
+
subagent_tools: frozenset[str] = field(default_factory=lambda: expand_tool_names(_TOOL_SETS["SUBAGENT_TOOLS"]))
|
|
172
185
|
|
|
173
186
|
|
|
174
187
|
@dataclass(frozen=True, slots=True)
|
|
@@ -259,9 +272,11 @@ class MiningSpec:
|
|
|
259
272
|
provenance: ProvenanceSpec = field(default_factory=ProvenanceSpec)
|
|
260
273
|
review: ReviewSpec = field(default_factory=ReviewSpec)
|
|
261
274
|
reentry_lookback: int = REENTRY_LOOKBACK
|
|
262
|
-
edit_tools: frozenset[str] = EDIT_TOOLS
|
|
263
|
-
plan_tools: frozenset[str] = PLAN_TOOLS
|
|
264
|
-
denial_excluded_tools: frozenset[str] =
|
|
275
|
+
edit_tools: frozenset[str] = field(default_factory=lambda: expand_tool_names(_TOOL_SETS["EDIT_TOOLS"]))
|
|
276
|
+
plan_tools: frozenset[str] = field(default_factory=lambda: expand_tool_names(_TOOL_SETS["PLAN_TOOLS"]))
|
|
277
|
+
denial_excluded_tools: frozenset[str] = field(
|
|
278
|
+
default_factory=lambda: expand_tool_names(_TOOL_SETS["DENIAL_EXCLUDED_TOOLS"])
|
|
279
|
+
)
|
|
265
280
|
|
|
266
281
|
|
|
267
282
|
def bump(signal: CandidateSignal, delta: float, reason: str) -> CandidateSignal:
|
|
@@ -108,9 +108,39 @@ def parse_event(data: Mapping[str, Any]) -> TranscriptEvent | None:
|
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
def parse_events_from_bytes(raw: bytes) -> list[TranscriptEvent]:
|
|
111
|
+
"""Parse a JSONL transcript byte buffer into typed native events.
|
|
112
|
+
|
|
113
|
+
Compositionality contract: parsing splits on newlines and folds each line
|
|
114
|
+
independently, so for any split of ``raw`` on a line boundary,
|
|
115
|
+
``parse_events_from_bytes(prefix) + parse_events_from_bytes(suffix)`` equals
|
|
116
|
+
``parse_events_from_bytes(prefix + suffix)`` — value equality on the views'
|
|
117
|
+
structural ``__eq__``. Each call owns its buffer; no state carries across calls.
|
|
118
|
+
Blank, undecodable, and non-mapping lines are skipped identically wherever the
|
|
119
|
+
split falls, and an unterminated final line parses the same as a terminated one.
|
|
120
|
+
A line that fails typed parsing — a JSON object missing a required field —
|
|
121
|
+
raises identically wherever the split falls: the side containing that line
|
|
122
|
+
raises, so the equation above applies to inputs that parse. Splits inside a
|
|
123
|
+
line are out of contract: a mid-line split is not a line boundary, and the
|
|
124
|
+
fragments may parse differently than the whole line.
|
|
125
|
+
|
|
126
|
+
Incremental consumers — tail parsers re-parsing an appended slice cut on a
|
|
127
|
+
newline boundary — may rely on this contract; it is pinned by the line-boundary
|
|
128
|
+
sweep in ``tests/test_parser.py``.
|
|
129
|
+
"""
|
|
111
130
|
return list(_native.parse_bytes(raw).events)
|
|
112
131
|
|
|
113
132
|
|
|
133
|
+
def parse_events(*lines: Mapping[str, Any]) -> list[TranscriptEvent]:
|
|
134
|
+
"""Parse raw transcript-line mappings into typed native events.
|
|
135
|
+
|
|
136
|
+
Serializes each mapping to a JSONL line and folds them through the native
|
|
137
|
+
backend — the same path :func:`parse_events_from_bytes` and production use — so
|
|
138
|
+
the result is a list of lazy views over a multi-line parse. Lines the tolerant
|
|
139
|
+
parser drops (a below-``MINYEAR`` timestamp) never materialize.
|
|
140
|
+
"""
|
|
141
|
+
return parse_events_from_bytes(b"\n".join(orjson.dumps(dict(line)) for line in lines))
|
|
142
|
+
|
|
143
|
+
|
|
114
144
|
def parse_print_result(raw: bytes) -> PrintResult:
|
|
115
145
|
"""Parse a 'claude -p --output-format json' payload into a :class:`~cc_transcript.models.PrintResult`.
|
|
116
146
|
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""Synthetic transcript builders — hand-assembled JSONL that parses into real events.
|
|
2
|
+
|
|
3
|
+
The v14 view model has no Python constructors, so tests and tools can no longer
|
|
4
|
+
hand-build ``UserEvent``/``ToolUseBlock``/… objects. Instead they synthesize the
|
|
5
|
+
JSONL a real transcript carries and parse it through the native backend — the exact
|
|
6
|
+
path production uses. The ``*_line`` builders assemble the raw envelope dicts
|
|
7
|
+
(mirroring the fields Claude Code emits), the block builders emit the
|
|
8
|
+
``message.content`` entries the parser lifts into block views, and
|
|
9
|
+
:func:`synthetic_user_event`/:func:`synthetic_assistant_event` round-trip a single
|
|
10
|
+
envelope through :func:`~cc_transcript.parser.parse_events` into one native event.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from datetime import UTC, datetime, timedelta
|
|
16
|
+
from typing import TYPE_CHECKING, Any
|
|
17
|
+
|
|
18
|
+
from cc_transcript.models import AssistantEvent, UserEvent
|
|
19
|
+
from cc_transcript.parser import parse_events
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Mapping, Sequence
|
|
23
|
+
|
|
24
|
+
BASE = datetime(2026, 1, 1, 12, 0, 0, tzinfo=UTC)
|
|
25
|
+
|
|
26
|
+
Content = str | list[dict[str, Any]]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def text_block(text: str) -> dict[str, Any]:
|
|
30
|
+
"""A ``text`` content block carrying ``text``."""
|
|
31
|
+
return {"type": "text", "text": text}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def thinking_block(thinking: str) -> dict[str, Any]:
|
|
35
|
+
"""A ``thinking`` content block carrying ``thinking``."""
|
|
36
|
+
return {"type": "thinking", "thinking": thinking}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def tool_use(id: str, name: str, input: Mapping[str, Any]) -> dict[str, Any]:
|
|
40
|
+
"""A ``tool_use`` content block invoking ``name`` with ``input`` under id ``id``."""
|
|
41
|
+
return {"type": "tool_use", "id": id, "name": name, "input": dict(input)}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def tool_result(tool_use_id: str, content: Content = "ok", *, is_error: bool = False) -> dict[str, Any]:
|
|
45
|
+
"""A ``tool_result`` content block for ``tool_use_id``; ``is_error`` marks a failed call."""
|
|
46
|
+
return {"type": "tool_result", "tool_use_id": tool_use_id, "content": content, "is_error": is_error}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def meta_fields(
|
|
50
|
+
uuid: str,
|
|
51
|
+
*,
|
|
52
|
+
session_id: str = "s",
|
|
53
|
+
timestamp: datetime = BASE,
|
|
54
|
+
secs: int = 0,
|
|
55
|
+
parent_uuid: str | None = None,
|
|
56
|
+
cwd: str | None = "/repo",
|
|
57
|
+
git_branch: str | None = "main",
|
|
58
|
+
version: str | None = "1.2.3",
|
|
59
|
+
is_sidechain: bool = False,
|
|
60
|
+
is_meta: bool = False,
|
|
61
|
+
entrypoint: str | None = "cli",
|
|
62
|
+
is_compact_summary: bool = False,
|
|
63
|
+
is_visible_in_transcript_only: bool = False,
|
|
64
|
+
user_type: str | None = None,
|
|
65
|
+
slug: str | None = None,
|
|
66
|
+
) -> dict[str, Any]:
|
|
67
|
+
"""The envelope-level fields the parser lifts into :class:`~cc_transcript.models.EntryMeta`.
|
|
68
|
+
|
|
69
|
+
Optional string fields are omitted when ``None`` so the parser's ``.get`` chains
|
|
70
|
+
reproduce a ``None`` attribute; booleans always serialize.
|
|
71
|
+
"""
|
|
72
|
+
fields: dict[str, Any] = {
|
|
73
|
+
"uuid": uuid,
|
|
74
|
+
"sessionId": session_id,
|
|
75
|
+
"timestamp": (timestamp + timedelta(seconds=secs)).isoformat(),
|
|
76
|
+
"isSidechain": is_sidechain,
|
|
77
|
+
"isMeta": is_meta,
|
|
78
|
+
"isCompactSummary": is_compact_summary,
|
|
79
|
+
"isVisibleInTranscriptOnly": is_visible_in_transcript_only,
|
|
80
|
+
}
|
|
81
|
+
for key, value in (
|
|
82
|
+
("parentUuid", parent_uuid),
|
|
83
|
+
("cwd", cwd),
|
|
84
|
+
("gitBranch", git_branch),
|
|
85
|
+
("version", version),
|
|
86
|
+
("entrypoint", entrypoint),
|
|
87
|
+
("userType", user_type),
|
|
88
|
+
("slug", slug),
|
|
89
|
+
):
|
|
90
|
+
if value is not None:
|
|
91
|
+
fields[key] = value
|
|
92
|
+
return fields
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def user_line(
|
|
96
|
+
uuid: str,
|
|
97
|
+
text: str = "",
|
|
98
|
+
*,
|
|
99
|
+
blocks: Sequence[dict[str, Any]] = (),
|
|
100
|
+
tool_use_result: Mapping[str, Any] | str | None = None,
|
|
101
|
+
tool_denial_kind: str | None = None,
|
|
102
|
+
interrupted: bool = False,
|
|
103
|
+
**meta_kw: Any,
|
|
104
|
+
) -> dict[str, Any]:
|
|
105
|
+
"""A ``type: user`` envelope; ``blocks`` are ``message.content`` entries.
|
|
106
|
+
|
|
107
|
+
With ``blocks`` the content is a list — ``text`` (when non-empty) becomes a
|
|
108
|
+
leading text block, exactly as a real transcript carries it. ``interrupted``
|
|
109
|
+
sets ``interruptedMessageId`` so the parser derives ``interrupted=True``.
|
|
110
|
+
"""
|
|
111
|
+
content: Content = ([text_block(text)] if text else []) + list(blocks) if blocks else text
|
|
112
|
+
line: dict[str, Any] = {"type": "user", "message": {"role": "user", "content": content}} | meta_fields(
|
|
113
|
+
uuid, **meta_kw
|
|
114
|
+
)
|
|
115
|
+
if tool_use_result is not None:
|
|
116
|
+
line["toolUseResult"] = tool_use_result
|
|
117
|
+
if tool_denial_kind is not None:
|
|
118
|
+
line["toolDenialKind"] = tool_denial_kind
|
|
119
|
+
if interrupted:
|
|
120
|
+
line["interruptedMessageId"] = f"imid-{uuid}"
|
|
121
|
+
return line
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def assistant_line(
|
|
125
|
+
uuid: str,
|
|
126
|
+
text: str = "",
|
|
127
|
+
*,
|
|
128
|
+
model: str = "claude-opus-4-7",
|
|
129
|
+
blocks: Sequence[dict[str, Any]] = (),
|
|
130
|
+
stop_reason: str | None = None,
|
|
131
|
+
usage: Mapping[str, Any] | None = None,
|
|
132
|
+
**meta_kw: Any,
|
|
133
|
+
) -> dict[str, Any]:
|
|
134
|
+
"""An ``type: assistant`` envelope; ``blocks`` are ``message.content`` entries."""
|
|
135
|
+
content = ([text_block(text)] if text else []) + list(blocks)
|
|
136
|
+
message: dict[str, Any] = {"role": "assistant", "model": model, "content": content}
|
|
137
|
+
if stop_reason is not None:
|
|
138
|
+
message["stop_reason"] = stop_reason
|
|
139
|
+
if usage is not None:
|
|
140
|
+
message["usage"] = dict(usage)
|
|
141
|
+
return {"type": "assistant", "message": message} | meta_fields(uuid, **meta_kw)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def mode_line(value: str, *, session_id: str = "s", channel: str = "mode") -> dict[str, Any]:
|
|
145
|
+
"""A ``mode``/``permission-mode`` envelope the parser lifts into :class:`~cc_transcript.models.ModeEvent`."""
|
|
146
|
+
key = "permissionMode" if channel == "permission-mode" else "mode"
|
|
147
|
+
return {"type": channel, "sessionId": session_id, key: value}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def system_line(subtype: str, *, content: str | None = None, level: str | None = None, **fields: Any) -> dict[str, Any]:
|
|
151
|
+
"""A ``type: system`` envelope; ``fields`` carry the subtype-specific detail keys."""
|
|
152
|
+
line: dict[str, Any] = {"type": "system", "subtype": subtype} | meta_fields(fields.pop("uuid", "sys")) | fields
|
|
153
|
+
if content is not None:
|
|
154
|
+
line["content"] = content
|
|
155
|
+
if level is not None:
|
|
156
|
+
line["level"] = level
|
|
157
|
+
return line
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def other_line(type: str, **fields: Any) -> dict[str, Any]:
|
|
161
|
+
"""An unmodeled-``type`` envelope lifted into :class:`~cc_transcript.models.OtherEvent`; ``raw`` is the line."""
|
|
162
|
+
return {"type": type} | fields
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def synthetic_user_event(
|
|
166
|
+
text: str = "",
|
|
167
|
+
*,
|
|
168
|
+
blocks: Sequence[Mapping[str, Any]] = (),
|
|
169
|
+
uuid: str = "synthetic",
|
|
170
|
+
**meta: Any,
|
|
171
|
+
) -> UserEvent:
|
|
172
|
+
"""Builds a native :class:`~cc_transcript.models.UserEvent` from text or content blocks.
|
|
173
|
+
|
|
174
|
+
Serializes a ``user`` envelope and parses it through the native backend, so the
|
|
175
|
+
result is a real view accepted anywhere parsed events are — including
|
|
176
|
+
:meth:`~cc_transcript.activity.SessionActivity.from_events`. Extra keywords forward
|
|
177
|
+
to the envelope metadata (``session_id``, ``timestamp``, …).
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
>>> synthetic_user_event("fix the bug", session_id="s1").text
|
|
181
|
+
'fix the bug'
|
|
182
|
+
|
|
183
|
+
Raises:
|
|
184
|
+
ValueError: When the envelope does not parse to exactly one ``UserEvent``.
|
|
185
|
+
"""
|
|
186
|
+
events = parse_events(user_line(uuid, text, blocks=[dict(block) for block in blocks], **meta))
|
|
187
|
+
match events:
|
|
188
|
+
case [UserEvent() as event]:
|
|
189
|
+
return event
|
|
190
|
+
raise ValueError(f"expected one UserEvent, got {[type(event).__name__ for event in events]}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def synthetic_assistant_event(
|
|
194
|
+
text: str = "",
|
|
195
|
+
*,
|
|
196
|
+
blocks: Sequence[Mapping[str, Any]] = (),
|
|
197
|
+
uuid: str = "synthetic",
|
|
198
|
+
**meta: Any,
|
|
199
|
+
) -> AssistantEvent:
|
|
200
|
+
"""Builds a native :class:`~cc_transcript.models.AssistantEvent` from text or content blocks.
|
|
201
|
+
|
|
202
|
+
Serializes an ``assistant`` envelope and parses it through the native backend, so
|
|
203
|
+
the result is a real view accepted anywhere parsed events are — including
|
|
204
|
+
:meth:`~cc_transcript.activity.SessionActivity.from_events`. Extra keywords forward
|
|
205
|
+
to the envelope metadata (``session_id``, ``timestamp``, …) and message fields
|
|
206
|
+
(``stop_reason``, ``usage``, ``model``).
|
|
207
|
+
|
|
208
|
+
Example:
|
|
209
|
+
>>> synthetic_assistant_event("on it", stop_reason="end_turn").text
|
|
210
|
+
'on it'
|
|
211
|
+
|
|
212
|
+
Raises:
|
|
213
|
+
ValueError: When the envelope does not parse to exactly one ``AssistantEvent``.
|
|
214
|
+
"""
|
|
215
|
+
events = parse_events(assistant_line(uuid, text, blocks=[dict(block) for block in blocks], **meta))
|
|
216
|
+
match events:
|
|
217
|
+
case [AssistantEvent() as event]:
|
|
218
|
+
return event
|
|
219
|
+
raise ValueError(f"expected one AssistantEvent, got {[type(event).__name__ for event in events]}")
|