cc-transcript 14.17.0__tar.gz → 14.19.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.17.0 → cc_transcript-14.19.0}/PKG-INFO +1 -1
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/__init__.py +1 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/__init__.pyi +2 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/_native.pyi +3 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/activity.py +176 -60
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/query.py +287 -54
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/pyproject.toml +1 -1
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/benches/parse.rs +10 -1
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/activity.rs +90 -7
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/python.rs +68 -22
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/Cargo.lock +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/Cargo.toml +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/LICENSE +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/README.md +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/builders.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/codex.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/command.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/context.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/corrections.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/cost.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/decision_store.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/decisions.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/discovery.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/evidence.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/facts.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/heartbeats.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/ids.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/judge/verdicts.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/ledger.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/literals.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/spec.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/models.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/nlp.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/notifications.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/parser.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/py.typed +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/render.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/synthetic.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/tools.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/cc_transcript/watch.py +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/Cargo.toml +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/blame.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/corrections.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/digest.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/facts.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/grep.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/list.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/mod.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/show.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/slice.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/stats.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/commands/watch.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/lib.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/main.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/output.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/target.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/cli/src/timearg.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/Cargo.toml +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/benches/render.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/benches/toolcall.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/actor.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/blame.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/buckets.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/discovery.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/lower.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/mod.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/parse.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/probe.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/protocol.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/codex/types.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/command.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/context.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/corrections.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/cost.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/discovery.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/facts.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/feedback.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/filter.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/gateway.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/ids.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/judge.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/lib.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/literals/command.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/literals/corrections.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/literals/feedback.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/literals/mining.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/literals/mod.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/literals/protocol.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/notifications.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/parse.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/protocol.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/pystr.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/query.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/render.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/rng.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/schema.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/sqlite.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/toolcall.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/types.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/value.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/core/src/watch.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/Cargo.toml +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/build.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/data/command_prefix_pins.tsv +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/actor_bridge.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/bin/stub_gen.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/codex.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/command.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/context.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/corrections.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/discovery.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/feedback.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/judge.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/lexicon.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/lib.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/mining.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/nlp.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/render.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/score.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/sqlite.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/toolcall.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/attachment.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/blocks.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/command.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/convert.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/dunder.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/events.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/meta.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/mod.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/print.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/store.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/system.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/toolcall.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/toolresult.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/views/transcript.rs +0 -0
- {cc_transcript-14.17.0 → cc_transcript-14.19.0}/rust/crates/py/src/watch.rs +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# isort: off
|
|
3
3
|
|
|
4
4
|
from cc_transcript.activity import (
|
|
5
|
+
ActivityLift as ActivityLift,
|
|
5
6
|
Edit as Edit,
|
|
6
7
|
SessionActivity as SessionActivity,
|
|
7
8
|
ToolUse as ToolUse,
|
|
@@ -305,6 +306,7 @@ EXPORTS: dict[str, str]
|
|
|
305
306
|
__all__ = [
|
|
306
307
|
"ASSISTANTS",
|
|
307
308
|
"Action",
|
|
309
|
+
"ActivityLift",
|
|
308
310
|
"ApiError",
|
|
309
311
|
"ApplyPatchCall",
|
|
310
312
|
"AskUserQuestionResult",
|
|
@@ -108,6 +108,7 @@ __all__ = [
|
|
|
108
108
|
"activity_hunk_overlap",
|
|
109
109
|
"activity_lift",
|
|
110
110
|
"activity_lift_from_events",
|
|
111
|
+
"activity_lift_tail",
|
|
111
112
|
"activity_overlap_between",
|
|
112
113
|
"activity_parse_show_hunks",
|
|
113
114
|
"bucket_events_from_events",
|
|
@@ -2295,6 +2296,8 @@ def activity_lift(path: builtins.str, max_events: builtins.int) -> dict[str, typ
|
|
|
2295
2296
|
|
|
2296
2297
|
def activity_lift_from_events(events: list[models.TranscriptEvent], opener_flags: typing.Optional[typing.Sequence[builtins.bool]] = None) -> list[dict[str, typing.Any]]: ...
|
|
2297
2298
|
|
|
2299
|
+
def activity_lift_tail(events: list[models.TranscriptEvent], opener_flags: typing.Optional[typing.Sequence[builtins.bool]] = None, open_turn: builtins.bool = False) -> dict[str, typing.Any]: ...
|
|
2300
|
+
|
|
2298
2301
|
def activity_overlap_between(incorrect: typing.Sequence[tuple[builtins.str, builtins.str]], correction: typing.Sequence[tuple[builtins.str, builtins.str]]) -> builtins.float: ...
|
|
2299
2302
|
|
|
2300
2303
|
def activity_parse_show_hunks(diff: builtins.str) -> builtins.list[tuple[builtins.str, builtins.str]]: ...
|
|
@@ -13,8 +13,8 @@ products.
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
from collections.abc import Callable
|
|
16
|
-
from dataclasses import dataclass
|
|
17
|
-
from typing import TYPE_CHECKING
|
|
16
|
+
from dataclasses import dataclass, replace
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
18
|
|
|
19
19
|
from cc_transcript import _native
|
|
20
20
|
from cc_transcript.discovery import TranscriptExpiredError, resolve
|
|
@@ -25,7 +25,7 @@ from cc_transcript.parser import parse
|
|
|
25
25
|
from cc_transcript.tools import edits_of, parse_tool_call, parse_tool_result
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
|
-
from collections.abc import Sequence
|
|
28
|
+
from collections.abc import Mapping, Sequence
|
|
29
29
|
from datetime import datetime
|
|
30
30
|
from pathlib import Path
|
|
31
31
|
|
|
@@ -181,64 +181,10 @@ class SessionActivity:
|
|
|
181
181
|
turn; everything else folds into the current one. Events before the
|
|
182
182
|
first qualifying prompt form turn 0 with prompt ``""``.
|
|
183
183
|
"""
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if user_classifier is native_user_classifier
|
|
188
|
-
else [bool(user_classifier(event)) if isinstance(event, UserEvent) else False for event in evs]
|
|
184
|
+
return cls(
|
|
185
|
+
session_id=session_id,
|
|
186
|
+
turns=ActivityLift(session_id, user_classifier=user_classifier).extend(events).turns,
|
|
189
187
|
)
|
|
190
|
-
tool_blocks = {
|
|
191
|
-
event_idx: tuple(block for block in event.blocks if isinstance(block, ToolUseBlock))
|
|
192
|
-
for event_idx, event in enumerate(evs)
|
|
193
|
-
if isinstance(event, AssistantEvent)
|
|
194
|
-
}
|
|
195
|
-
tool_block_positions: dict[int, int] = {}
|
|
196
|
-
turns: list[Turn] = []
|
|
197
|
-
for index, skeleton in enumerate(_native.activity_lift_from_events(evs, opener_flags)):
|
|
198
|
-
tool_uses: list[ToolUse] = []
|
|
199
|
-
for use in skeleton["tool_uses"]:
|
|
200
|
-
event_idx = use["event_idx"]
|
|
201
|
-
event = evs[event_idx]
|
|
202
|
-
block_position = tool_block_positions.get(event_idx, 0)
|
|
203
|
-
block = tool_blocks[event_idx][block_position]
|
|
204
|
-
tool_block_positions[event_idx] = block_position + 1
|
|
205
|
-
result_event_idx = use["result_event_idx"]
|
|
206
|
-
if result_event_idx is None:
|
|
207
|
-
result = None
|
|
208
|
-
result_ts = None
|
|
209
|
-
else:
|
|
210
|
-
result_event = evs[result_event_idx]
|
|
211
|
-
result = next(
|
|
212
|
-
candidate
|
|
213
|
-
for candidate in reversed(result_event.blocks)
|
|
214
|
-
if isinstance(candidate, ToolResultBlock) and candidate.tool_use_id == use["tool_use_id"]
|
|
215
|
-
)
|
|
216
|
-
result_ts = result_event.meta.timestamp
|
|
217
|
-
call = parse_tool_call(block.name, block.input, on_error="other")
|
|
218
|
-
tool_uses.append(
|
|
219
|
-
ToolUse(
|
|
220
|
-
ref=EventRef(session_id, event.meta.uuid, block.id),
|
|
221
|
-
call=call,
|
|
222
|
-
result=result,
|
|
223
|
-
result_ts=result_ts,
|
|
224
|
-
edits=edits_of(call),
|
|
225
|
-
turn_index=index,
|
|
226
|
-
ts=event.meta.timestamp,
|
|
227
|
-
)
|
|
228
|
-
)
|
|
229
|
-
started_idx = skeleton["started_idx"]
|
|
230
|
-
ended_idx = skeleton["ended_idx"]
|
|
231
|
-
turns.append(
|
|
232
|
-
Turn(
|
|
233
|
-
index=index,
|
|
234
|
-
prompt=skeleton["prompt"],
|
|
235
|
-
started_at=None if started_idx is None else evs[started_idx].meta.timestamp,
|
|
236
|
-
ended_at=None if ended_idx is None else evs[ended_idx].meta.timestamp,
|
|
237
|
-
events=tuple(evs[skeleton["start"] : skeleton["end"]]),
|
|
238
|
-
tool_uses=tuple(tool_uses),
|
|
239
|
-
)
|
|
240
|
-
)
|
|
241
|
-
return cls(session_id=session_id, turns=tuple(turns))
|
|
242
188
|
|
|
243
189
|
@classmethod
|
|
244
190
|
def from_session(
|
|
@@ -326,6 +272,176 @@ class SessionActivity:
|
|
|
326
272
|
return tuple(same_turn + later)
|
|
327
273
|
|
|
328
274
|
|
|
275
|
+
class ActivityLift:
|
|
276
|
+
"""A session's lift that grows with its transcript.
|
|
277
|
+
|
|
278
|
+
Each :meth:`extend` folds the events appended since the previous call into
|
|
279
|
+
the activity lifted so far: closed turns are kept, the open turn grows by
|
|
280
|
+
the appended events alone, and a tool use whose result arrives in the tail
|
|
281
|
+
is re-paired wherever it sits. The result always equals
|
|
282
|
+
:meth:`SessionActivity.from_events` over every event fed so far. A step
|
|
283
|
+
costs the appended events, the tool uses they re-pair, and copies of the
|
|
284
|
+
turn tuple and of the open turn's event and tool-use tuples; it never
|
|
285
|
+
re-derives the session.
|
|
286
|
+
|
|
287
|
+
Feed only the events appended since the previous call: nothing checks
|
|
288
|
+
provenance, so events fed twice lift twice. The contract is append-only:
|
|
289
|
+
a cursor's transcript is assumed never rewritten behind the events it has
|
|
290
|
+
been fed, and a transcript rewritten in place, rather than appended to,
|
|
291
|
+
needs a new cursor. Claude Code and Codex transcripts satisfy it.
|
|
292
|
+
|
|
293
|
+
The classifier is read once, at construction, and must be deterministic
|
|
294
|
+
and event-only: its answer for an event has to stay the same as later
|
|
295
|
+
events are appended, since turns lifted under it are never revisited.
|
|
296
|
+
|
|
297
|
+
Example:
|
|
298
|
+
>>> lift = ActivityLift(session_id)
|
|
299
|
+
>>> activity = lift.extend(events)
|
|
300
|
+
>>> lift.extend(appended) == SessionActivity.from_events(session_id, [*events, *appended])
|
|
301
|
+
True
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
__slots__ = ("_activity", "_results", "_session_id", "_user_classifier", "_uses")
|
|
305
|
+
|
|
306
|
+
def __init__(self, session_id: SessionId, *, user_classifier: UserClassifier = native_user_classifier) -> None:
|
|
307
|
+
self._session_id = session_id
|
|
308
|
+
self._user_classifier = user_classifier
|
|
309
|
+
self._activity = SessionActivity(session_id=session_id, turns=())
|
|
310
|
+
self._uses: dict[ToolUseId, tuple[tuple[int, int], ...]] = {}
|
|
311
|
+
self._results: dict[ToolUseId, UserEvent] = {}
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def session_id(self) -> SessionId:
|
|
315
|
+
"""The session being lifted."""
|
|
316
|
+
return self._session_id
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
def user_classifier(self) -> UserClassifier:
|
|
320
|
+
"""Decides which user events open turns."""
|
|
321
|
+
return self._user_classifier
|
|
322
|
+
|
|
323
|
+
@property
|
|
324
|
+
def activity(self) -> SessionActivity:
|
|
325
|
+
"""Everything lifted so far."""
|
|
326
|
+
return self._activity
|
|
327
|
+
|
|
328
|
+
def extend(self, events: Sequence[TranscriptEvent]) -> SessionActivity:
|
|
329
|
+
"""Lifts ``events``, appended after everything fed so far, into :attr:`activity`."""
|
|
330
|
+
tail = list(events)
|
|
331
|
+
if not tail:
|
|
332
|
+
return self._activity
|
|
333
|
+
opener_flags = (
|
|
334
|
+
None
|
|
335
|
+
if self._user_classifier is native_user_classifier
|
|
336
|
+
else [bool(self._user_classifier(event)) if isinstance(event, UserEvent) else False for event in tail]
|
|
337
|
+
)
|
|
338
|
+
turns = list(self._activity.turns)
|
|
339
|
+
lifted = _native.activity_lift_tail(tail, opener_flags, open_turn=bool(turns))
|
|
340
|
+
first = len(turns) - 1 if lifted["continued"] else len(turns)
|
|
341
|
+
grown = [
|
|
342
|
+
lift_turn(self._session_id, first + offset, skeleton, tail, self._results)
|
|
343
|
+
for offset, skeleton in enumerate(lifted["turns"])
|
|
344
|
+
]
|
|
345
|
+
if lifted["continued"]:
|
|
346
|
+
turns[-1] = continued(turns[-1], grown[0])
|
|
347
|
+
turns.extend(grown[1:])
|
|
348
|
+
else:
|
|
349
|
+
turns.extend(grown)
|
|
350
|
+
repairs: dict[int, dict[int, UserEvent]] = {}
|
|
351
|
+
for tool_use_id, result_idx in lifted["results"]:
|
|
352
|
+
for turn_index, position in self._uses.get(tool_use_id, ()):
|
|
353
|
+
repairs.setdefault(turn_index, {})[position] = tail[result_idx]
|
|
354
|
+
self._results[tool_use_id] = tail[result_idx]
|
|
355
|
+
for turn_index, at in repairs.items():
|
|
356
|
+
turns[turn_index] = repaired(turns[turn_index], at)
|
|
357
|
+
for turn in grown:
|
|
358
|
+
base = len(turns[turn.index].tool_uses) - len(turn.tool_uses)
|
|
359
|
+
for position, use in enumerate(turn.tool_uses, base):
|
|
360
|
+
self._uses[use.ref.tool_use_id] = (*self._uses.get(use.ref.tool_use_id, ()), (turn.index, position))
|
|
361
|
+
self._activity = SessionActivity(session_id=self._session_id, turns=tuple(turns))
|
|
362
|
+
return self._activity
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def lift_turn(
|
|
366
|
+
session_id: SessionId,
|
|
367
|
+
index: int,
|
|
368
|
+
skeleton: dict[str, Any],
|
|
369
|
+
evs: Sequence[TranscriptEvent],
|
|
370
|
+
prior_results: Mapping[ToolUseId, UserEvent],
|
|
371
|
+
) -> Turn:
|
|
372
|
+
events = tuple(evs[skeleton["start"] : skeleton["end"]])
|
|
373
|
+
calls = [
|
|
374
|
+
(event, block)
|
|
375
|
+
for event in events
|
|
376
|
+
if isinstance(event, AssistantEvent)
|
|
377
|
+
for block in event.blocks
|
|
378
|
+
if isinstance(block, ToolUseBlock)
|
|
379
|
+
]
|
|
380
|
+
started_idx = skeleton["started_idx"]
|
|
381
|
+
ended_idx = skeleton["ended_idx"]
|
|
382
|
+
return Turn(
|
|
383
|
+
index=index,
|
|
384
|
+
prompt=skeleton["prompt"],
|
|
385
|
+
started_at=None if started_idx is None else evs[started_idx].meta.timestamp,
|
|
386
|
+
ended_at=None if ended_idx is None else evs[ended_idx].meta.timestamp,
|
|
387
|
+
events=events,
|
|
388
|
+
tool_uses=tuple(
|
|
389
|
+
lift_tool_use(
|
|
390
|
+
session_id,
|
|
391
|
+
index,
|
|
392
|
+
event,
|
|
393
|
+
block,
|
|
394
|
+
prior_results.get(block.id) if (result_idx := use["result_event_idx"]) is None else evs[result_idx],
|
|
395
|
+
)
|
|
396
|
+
for (event, block), use in zip(calls, skeleton["tool_uses"], strict=True)
|
|
397
|
+
),
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def lift_tool_use(
|
|
402
|
+
session_id: SessionId, turn_index: int, event: AssistantEvent, block: ToolUseBlock, result_event: UserEvent | None
|
|
403
|
+
) -> ToolUse:
|
|
404
|
+
call = parse_tool_call(block.name, block.input, on_error="other")
|
|
405
|
+
return ToolUse(
|
|
406
|
+
ref=EventRef(session_id, event.meta.uuid, block.id),
|
|
407
|
+
call=call,
|
|
408
|
+
result=None if result_event is None else result_block(result_event, block.id),
|
|
409
|
+
result_ts=None if result_event is None else result_event.meta.timestamp,
|
|
410
|
+
edits=edits_of(call),
|
|
411
|
+
turn_index=turn_index,
|
|
412
|
+
ts=event.meta.timestamp,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def result_block(event: UserEvent, tool_use_id: ToolUseId) -> ToolResultBlock:
|
|
417
|
+
return next(
|
|
418
|
+
candidate
|
|
419
|
+
for candidate in reversed(event.blocks)
|
|
420
|
+
if isinstance(candidate, ToolResultBlock) and candidate.tool_use_id == tool_use_id
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def continued(turn: Turn, tail: Turn) -> Turn:
|
|
425
|
+
return replace(
|
|
426
|
+
turn,
|
|
427
|
+
started_at=tail.started_at if turn.started_at is None else turn.started_at,
|
|
428
|
+
ended_at=turn.ended_at if tail.ended_at is None else tail.ended_at,
|
|
429
|
+
events=turn.events + tail.events,
|
|
430
|
+
tool_uses=turn.tool_uses + tail.tool_uses,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def repaired(turn: Turn, at: Mapping[int, UserEvent]) -> Turn:
|
|
435
|
+
uses = list(turn.tool_uses)
|
|
436
|
+
for position, event in at.items():
|
|
437
|
+
uses[position] = replace(
|
|
438
|
+
uses[position],
|
|
439
|
+
result=result_block(event, uses[position].ref.tool_use_id),
|
|
440
|
+
result_ts=event.meta.timestamp,
|
|
441
|
+
)
|
|
442
|
+
return replace(turn, tool_uses=tuple(uses))
|
|
443
|
+
|
|
444
|
+
|
|
329
445
|
def hunk_overlap(a: Hunk, b: Hunk) -> float:
|
|
330
446
|
"""The fraction of ``a.new``'s non-empty lines present in ``b.old``.
|
|
331
447
|
|