cc-transcript 14.6.0__tar.gz → 14.6.1__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.6.0 → cc_transcript-14.6.1}/PKG-INFO +1 -1
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/__init__.py +7 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/__init__.pyi +13 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/_native.pyi +15 -5
- cc_transcript-14.6.1/cc_transcript/codex.py +212 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/discovery.py +7 -3
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/judge/verdicts.py +6 -1
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/pyproject.toml +1 -1
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/discovery.rs +30 -12
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/probe.rs +21 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/gateway.rs +9 -0
- cc_transcript-14.6.1/rust/crates/py/src/codex.rs +144 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/lib.rs +1 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/python.rs +20 -6
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/transcript.rs +11 -5
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/Cargo.lock +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/Cargo.toml +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/LICENSE +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/README.md +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/activity.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/activity_probe.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/builders.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/command.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/context.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/corrections.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/cost.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/decisions.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/disktruth.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/evidence.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/extract/__init__.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/extract/correct.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/facts.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/filterspec.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/heartbeats.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/ids.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/judge/__init__.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/judge/llm.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/judge/similar.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/ledger.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/literals.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/__init__.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/candidates.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/confidence.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/engine.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/formats.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/sampling.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/signals.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/sourcekind.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/spec.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/mining/store.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/models.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/nlp.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/notifications.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/parser.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/py.typed +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/query.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/render.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/__init__.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/buckets.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/data/afinn-en-165.tsv +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/data/domain_overrides.tsv +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/data/en-ewt.udpipe +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/engine.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/lexicon.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/sentiment/scorespec.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/synthetic.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/tools.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/cc_transcript/watch.py +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/Cargo.toml +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/corrections.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/digest.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/facts.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/grep.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/list.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/mod.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/scratchpad.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/show.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/slice.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/stats.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/commands/watch.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/lib.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/main.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/output.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/cli/src/target.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/Cargo.toml +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/benches/parse.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/benches/render.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/benches/toolcall.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/activity.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/actor.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/buckets.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/lower.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/mod.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/parse.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/protocol.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/codex/types.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/command.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/context.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/corrections.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/cost.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/discovery.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/facts.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/feedback.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/filter.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/ids.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/lib.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/literals/command.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/literals/corrections.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/literals/feedback.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/literals/mining.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/literals/mod.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/literals/protocol.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/notifications.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/parse.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/protocol.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/pystr.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/query.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/render.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/sqlite.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/toolcall.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/types.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/value.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/core/src/watch.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/Cargo.toml +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/build.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/data/command_prefix_pins.tsv +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/actor_bridge.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/bin/stub_gen.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/command.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/context.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/corrections.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/discovery.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/feedback.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/lexicon.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/mining.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/nlp.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/render.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/score.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/sqlite.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/toolcall.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/attachment.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/blocks.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/command.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/convert.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/dunder.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/events.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/meta.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/mod.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/print.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/store.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/system.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/toolcall.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/views/toolresult.rs +0 -0
- {cc_transcript-14.6.0 → cc_transcript-14.6.1}/rust/crates/py/src/watch.rs +0 -0
|
@@ -181,6 +181,13 @@ EXPORTS: dict[str, str] = {
|
|
|
181
181
|
"resolve",
|
|
182
182
|
"subagent_paths",
|
|
183
183
|
),
|
|
184
|
+
"cc_transcript.codex": (
|
|
185
|
+
"CodexPendingItem",
|
|
186
|
+
"CodexRollout",
|
|
187
|
+
"CodexSessionInfo",
|
|
188
|
+
"CodexUsage",
|
|
189
|
+
"Lifecycle",
|
|
190
|
+
),
|
|
184
191
|
"cc_transcript.activity": (
|
|
185
192
|
"Edit",
|
|
186
193
|
"SessionActivity",
|
|
@@ -27,6 +27,14 @@ from cc_transcript.builders import (
|
|
|
27
27
|
keep_only as keep_only,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
from cc_transcript.codex import (
|
|
31
|
+
CodexPendingItem as CodexPendingItem,
|
|
32
|
+
CodexRollout as CodexRollout,
|
|
33
|
+
CodexSessionInfo as CodexSessionInfo,
|
|
34
|
+
CodexUsage as CodexUsage,
|
|
35
|
+
Lifecycle as Lifecycle,
|
|
36
|
+
)
|
|
37
|
+
|
|
30
38
|
from cc_transcript.command import (
|
|
31
39
|
Command as Command,
|
|
32
40
|
CommandLine as CommandLine,
|
|
@@ -302,6 +310,10 @@ __all__ = [
|
|
|
302
310
|
"CacheCreation",
|
|
303
311
|
"CandidatePair",
|
|
304
312
|
"CcVersion",
|
|
313
|
+
"CodexPendingItem",
|
|
314
|
+
"CodexRollout",
|
|
315
|
+
"CodexSessionInfo",
|
|
316
|
+
"CodexUsage",
|
|
305
317
|
"Command",
|
|
306
318
|
"CommandLine",
|
|
307
319
|
"CommandLineQuery",
|
|
@@ -342,6 +354,7 @@ __all__ = [
|
|
|
342
354
|
"HydratedWindow",
|
|
343
355
|
"InitInfo",
|
|
344
356
|
"JUNK_USER_MESSAGE_RE",
|
|
357
|
+
"Lifecycle",
|
|
345
358
|
"McpServer",
|
|
346
359
|
"ModeEvent",
|
|
347
360
|
"ModelPricing",
|
|
@@ -104,6 +104,9 @@ __all__ = [
|
|
|
104
104
|
"activity_lift_from_events",
|
|
105
105
|
"bucket_events_from_events",
|
|
106
106
|
"cli_main",
|
|
107
|
+
"codex_discover",
|
|
108
|
+
"codex_resolve",
|
|
109
|
+
"codex_session_info",
|
|
107
110
|
"command_parse",
|
|
108
111
|
"command_prefixes",
|
|
109
112
|
"context_capture_window",
|
|
@@ -1941,15 +1944,14 @@ class Transcript:
|
|
|
1941
1944
|
The parsed events of a single transcript file, backed by the native parse
|
|
1942
1945
|
output; ``events`` materializes lazy views on access.
|
|
1943
1946
|
|
|
1944
|
-
One parse owns one ``Arc<Vec<Entry>>``
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
:class:`~watch.WatchEvent` stream is exempt — its tailer builds a
|
|
1948
|
-
one-entry Arc per yielded event, so a held watch event pins only itself.
|
|
1947
|
+
One parse owns one ``Arc<Vec<Entry>>`` shared by every view, so retaining any
|
|
1948
|
+
view keeps the whole parse's entries alive; the live
|
|
1949
|
+
:class:`~watch.WatchEvent` stream is exempt (one-entry Arc each).
|
|
1949
1950
|
|
|
1950
1951
|
Attributes:
|
|
1951
1952
|
path: The transcript's path on disk; None for a bytes parse.
|
|
1952
1953
|
mtime: The transcript's modification time when parsed.
|
|
1954
|
+
provider: The transcript's source provider: "claude" or "codex".
|
|
1953
1955
|
events: The parsed events, in file order.
|
|
1954
1956
|
"""
|
|
1955
1957
|
@property
|
|
@@ -1957,6 +1959,8 @@ class Transcript:
|
|
|
1957
1959
|
@property
|
|
1958
1960
|
def mtime(self) -> builtins.float: ...
|
|
1959
1961
|
@property
|
|
1962
|
+
def provider(self) -> typing.Literal["claude", "codex"]: ...
|
|
1963
|
+
@property
|
|
1960
1964
|
def events(self) -> EventList: ...
|
|
1961
1965
|
|
|
1962
1966
|
@typing.final
|
|
@@ -2193,6 +2197,12 @@ def cli_main() -> builtins.int:
|
|
|
2193
2197
|
run, and the exit code returned verbatim for the wrapper's `sys.exit`.
|
|
2194
2198
|
"""
|
|
2195
2199
|
|
|
2200
|
+
def codex_discover(root: typing.Optional[builtins.str | os.PathLike | pathlib.Path] = None) -> builtins.list[tuple[pathlib.Path, builtins.str, builtins.bool]]: ...
|
|
2201
|
+
|
|
2202
|
+
def codex_resolve(session_id: builtins.str, root: typing.Optional[builtins.str | os.PathLike | pathlib.Path] = None) -> typing.Optional[pathlib.Path]: ...
|
|
2203
|
+
|
|
2204
|
+
def codex_session_info(path: builtins.str) -> dict[str, typing.Any]: ...
|
|
2205
|
+
|
|
2196
2206
|
def command_parse(command: builtins.str) -> dict[str, typing.Any]: ...
|
|
2197
2207
|
|
|
2198
2208
|
def command_prefixes(commands: typing.Sequence[builtins.str]) -> builtins.list[builtins.list[builtins.str]]: ...
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""The codex session surface: discovery, resolution, and per-rollout session info.
|
|
2
|
+
|
|
3
|
+
A sync facade over the native codex functions, mirroring
|
|
4
|
+
:mod:`cc_transcript.discovery` for the ``~/.codex/sessions`` tree. The sessions-root
|
|
5
|
+
default is a facade concern; the native side scans exactly the root it is handed.
|
|
6
|
+
|
|
7
|
+
There is no parse function here: :func:`cc_transcript.parse` is already
|
|
8
|
+
provider-transparent — it sniffs a codex rollout, lowers it into the native event
|
|
9
|
+
model, and yields the same :class:`~cc_transcript.models.Transcript` view a Claude
|
|
10
|
+
transcript produces, with ``provider == "codex"``. This module adds only the codex
|
|
11
|
+
identity, lifecycle, and token-usage surface that the lowered event stream drops.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import TYPE_CHECKING, Literal
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from cc_transcript.ids import SessionId
|
|
22
|
+
|
|
23
|
+
SESSIONS_ROOT = Path.home() / ".codex" / "sessions"
|
|
24
|
+
|
|
25
|
+
Lifecycle = Literal["uninitialized", "no_instrumentation", "open", "completed", "aborted"]
|
|
26
|
+
"""A codex rollout's turn-lifecycle state.
|
|
27
|
+
|
|
28
|
+
``uninitialized`` — no content at all; ``no_instrumentation`` — content but no
|
|
29
|
+
``task_started``/``task_complete`` brackets; ``open`` — the latest turn is still
|
|
30
|
+
running; ``completed`` — the latest turn finished; ``aborted`` — the latest turn was
|
|
31
|
+
interrupted.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True, slots=True)
|
|
36
|
+
class CodexRollout:
|
|
37
|
+
"""A rollout found in the codex sessions tree.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
path: The rollout's path.
|
|
41
|
+
session_id: The session id encoded in the rollout filename.
|
|
42
|
+
compressed: Whether the rollout is a ``.jsonl.zst`` file.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
path: Path
|
|
46
|
+
session_id: str
|
|
47
|
+
compressed: bool
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True, slots=True)
|
|
51
|
+
class CodexUsage:
|
|
52
|
+
"""Session-level token totals lowered from a codex rollout.
|
|
53
|
+
|
|
54
|
+
The totals are the last observed cumulative ``total_token_usage`` fields — codex
|
|
55
|
+
``token_count`` events carry running totals, not per-turn deltas — so this is the
|
|
56
|
+
session's final accounting, not a sum.
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
input_tokens: Total input tokens, or None when never reported.
|
|
60
|
+
cached_input_tokens: Cached input tokens, or None when never reported.
|
|
61
|
+
output_tokens: Output tokens, or None when never reported.
|
|
62
|
+
reasoning_output_tokens: Reasoning output tokens, or None when never reported.
|
|
63
|
+
total_tokens: Total tokens, or None when never reported.
|
|
64
|
+
model_context_window: The model's context window, or None when never reported.
|
|
65
|
+
token_count_events: The number of ``token_count`` events observed.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
input_tokens: int | None
|
|
69
|
+
cached_input_tokens: int | None
|
|
70
|
+
output_tokens: int | None
|
|
71
|
+
reasoning_output_tokens: int | None
|
|
72
|
+
total_tokens: int | None
|
|
73
|
+
model_context_window: int | None
|
|
74
|
+
token_count_events: int
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@dataclass(frozen=True, slots=True)
|
|
78
|
+
class CodexPendingItem:
|
|
79
|
+
"""A dangling tool call in the rollout's open turn.
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
tool_use_id: The call's ``call_id``.
|
|
83
|
+
name: The tool's name, such as ``exec``.
|
|
84
|
+
kind: How the call contributes to the verdict; always ``mid_tool`` for codex.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
tool_use_id: str | None
|
|
88
|
+
name: str
|
|
89
|
+
kind: str
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True, slots=True)
|
|
93
|
+
class CodexSessionInfo:
|
|
94
|
+
"""The identity, lifecycle, and usage of one codex rollout.
|
|
95
|
+
|
|
96
|
+
Attributes:
|
|
97
|
+
rollout_thread_id: This rollout's own thread id (the session-meta ``id``).
|
|
98
|
+
session_id: The logical session id; a forked rollout inherits its origin's.
|
|
99
|
+
parent_thread_id: The spawning thread's id for a subagent rollout, else None.
|
|
100
|
+
forked_from_id: The origin thread's id when this rollout is a fork, else None.
|
|
101
|
+
cwd: The working directory recorded in the session meta.
|
|
102
|
+
originator: What launched the session, such as ``codex_exec``.
|
|
103
|
+
cli_version: The codex CLI version that wrote the rollout.
|
|
104
|
+
model_provider: The model provider, such as ``openai``.
|
|
105
|
+
lifecycle: The latest turn's lifecycle state.
|
|
106
|
+
turn_id: The latest turn's id, or None before any turn brackets.
|
|
107
|
+
pending: The dangling tool calls in an open turn, in document order.
|
|
108
|
+
last_event_epoch: The latest envelope timestamp as epoch seconds, or None.
|
|
109
|
+
usage: The session token totals, or None when no ``token_count`` event occurred.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
rollout_thread_id: str | None
|
|
113
|
+
session_id: str | None
|
|
114
|
+
parent_thread_id: str | None
|
|
115
|
+
forked_from_id: str | None
|
|
116
|
+
cwd: str | None
|
|
117
|
+
originator: str | None
|
|
118
|
+
cli_version: str | None
|
|
119
|
+
model_provider: str | None
|
|
120
|
+
lifecycle: Lifecycle
|
|
121
|
+
turn_id: str | None
|
|
122
|
+
pending: tuple[CodexPendingItem, ...]
|
|
123
|
+
last_event_epoch: int | None
|
|
124
|
+
usage: CodexUsage | None
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def sessions_root(root: Path | None = None) -> Path:
|
|
128
|
+
"""The codex sessions root, defaulting to ``~/.codex/sessions``.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
root: An explicit root; when None, :data:`SESSIONS_ROOT`.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
The root that :func:`discover` and :func:`find_transcript` scan.
|
|
135
|
+
"""
|
|
136
|
+
return root or SESSIONS_ROOT
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def discover(root: Path | None = None) -> tuple[CodexRollout, ...]:
|
|
140
|
+
"""Every codex rollout under ``root``, newest first.
|
|
141
|
+
|
|
142
|
+
Rollouts live as ``rollout-<timestamp>-<session_id>.jsonl`` (or ``.jsonl.zst``)
|
|
143
|
+
files under the date-sharded sessions tree. Each result preserves its path,
|
|
144
|
+
session id, and compression state.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
root: The sessions root; when None, :func:`sessions_root`.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
The rollouts, newest first; ``()`` when the root does not exist.
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
>>> for rollout in discover():
|
|
154
|
+
... print(rollout.session_id, rollout.path)
|
|
155
|
+
"""
|
|
156
|
+
from cc_transcript import _native
|
|
157
|
+
|
|
158
|
+
return tuple(CodexRollout(*rollout) for rollout in _native.codex_discover(sessions_root(root)))
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def find_transcript(session_id: SessionId, root: Path | None = None) -> Path | None:
|
|
162
|
+
"""Locates ``session_id``'s rollout under the codex sessions tree.
|
|
163
|
+
|
|
164
|
+
Only uncompressed ``.jsonl`` rollouts resolve. Compressed rollouts appear in
|
|
165
|
+
``discover()`` but do not resolve until zstd support lands. The newest wins among
|
|
166
|
+
same-id uncompressed rollouts.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
session_id: The codex session id, as it appears in the rollout filename.
|
|
170
|
+
root: The sessions root; when None, :func:`sessions_root`.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The rollout path, or None when no rollout carries that session id.
|
|
174
|
+
"""
|
|
175
|
+
from cc_transcript import _native
|
|
176
|
+
|
|
177
|
+
return _native.codex_resolve(session_id, sessions_root(root))
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def session_info(path: Path) -> CodexSessionInfo:
|
|
181
|
+
"""The identity, lifecycle, and token usage of the rollout at ``path``.
|
|
182
|
+
|
|
183
|
+
The native extension reads and folds the rollout in one pass, returning the
|
|
184
|
+
session identity, the turn lifecycle, any dangling calls, and the token totals.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
path: The ``.jsonl`` rollout to inspect.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
The rollout's session info.
|
|
191
|
+
"""
|
|
192
|
+
from cc_transcript import _native
|
|
193
|
+
|
|
194
|
+
payload = _native.codex_session_info(str(path))
|
|
195
|
+
identity = payload["identity"]
|
|
196
|
+
lifecycle = payload["lifecycle"]
|
|
197
|
+
usage = payload["usage"]
|
|
198
|
+
return CodexSessionInfo(
|
|
199
|
+
rollout_thread_id=identity["rollout_thread_id"],
|
|
200
|
+
session_id=identity["session_id"],
|
|
201
|
+
parent_thread_id=identity["parent_thread_id"],
|
|
202
|
+
forked_from_id=identity["forked_from_id"],
|
|
203
|
+
cwd=identity["cwd"],
|
|
204
|
+
originator=identity["originator"],
|
|
205
|
+
cli_version=identity["cli_version"],
|
|
206
|
+
model_provider=identity["model_provider"],
|
|
207
|
+
lifecycle=lifecycle["state"],
|
|
208
|
+
turn_id=lifecycle["turn_id"],
|
|
209
|
+
pending=tuple(CodexPendingItem(**item) for item in payload["pending"]),
|
|
210
|
+
last_event_epoch=payload["last_event_epoch"],
|
|
211
|
+
usage=CodexUsage(**usage) if usage is not None else None,
|
|
212
|
+
)
|
|
@@ -96,7 +96,8 @@ def resolve(session_id: SessionId, *, root: Path | None = None) -> Path | None:
|
|
|
96
96
|
A successful lookup is memoized in ``TRANSCRIPT_MEMO`` under
|
|
97
97
|
``(session_id, resolved root)`` and revalidated on the next hit, so a
|
|
98
98
|
repeated probe skips the recursive scan while a pruned transcript still
|
|
99
|
-
forces a fresh one.
|
|
99
|
+
forces a fresh one. When the projects tree yields nothing, the codex
|
|
100
|
+
sessions tree is tried as a fallback, so a codex session id resolves too.
|
|
100
101
|
|
|
101
102
|
Returns:
|
|
102
103
|
The newest-mtime real path, or None when no transcript exists.
|
|
@@ -105,8 +106,11 @@ def resolve(session_id: SessionId, *, root: Path | None = None) -> Path | None:
|
|
|
105
106
|
key = (session_id, base.resolve())
|
|
106
107
|
if (cached := TRANSCRIPT_MEMO.get(key)) is not None and cached.exists():
|
|
107
108
|
return cached
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
hit = _native.discovery_find_transcript(base, session_id)
|
|
110
|
+
if hit is None:
|
|
111
|
+
from cc_transcript import codex
|
|
112
|
+
|
|
113
|
+
return codex.find_transcript(session_id)
|
|
110
114
|
TRANSCRIPT_MEMO[key] = hit
|
|
111
115
|
return hit
|
|
112
116
|
|
|
@@ -334,6 +334,7 @@ async def run_verdicts[V](
|
|
|
334
334
|
"""
|
|
335
335
|
counts = {"judged": 0, "failed": 0}
|
|
336
336
|
limiter = asyncio.Semaphore(concurrency)
|
|
337
|
+
persister = asyncio.Lock()
|
|
337
338
|
|
|
338
339
|
async def worker(row: Mapping[str, object]) -> None:
|
|
339
340
|
async with limiter:
|
|
@@ -342,7 +343,11 @@ async def run_verdicts[V](
|
|
|
342
343
|
except JudgeError:
|
|
343
344
|
counts["failed"] += 1
|
|
344
345
|
return
|
|
345
|
-
|
|
346
|
+
# The store's transaction is exclusive by contract; persists serialize on
|
|
347
|
+
# this lock so concurrent workers never race record_verdict into a
|
|
348
|
+
# TransactionConflictError. Judge concurrency is untouched.
|
|
349
|
+
async with persister:
|
|
350
|
+
await persist(row, verdict)
|
|
346
351
|
counts["judged"] += 1
|
|
347
352
|
|
|
348
353
|
async with asyncio.TaskGroup() as tg:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cc-transcript"
|
|
3
|
-
version = "14.6.
|
|
3
|
+
version = "14.6.1" # informational — releases stamp the version from the git tag
|
|
4
4
|
description = "Grep every Claude Code session you've ever run."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "PolyForm-Noncommercial-1.0.0"
|
|
@@ -46,12 +46,7 @@ pub fn resolve(session_id: &str, root: &Path) -> Option<PathBuf> {
|
|
|
46
46
|
found
|
|
47
47
|
.iter()
|
|
48
48
|
.find(|rollout| rollout.session_id == session_id && !rollout.compressed)
|
|
49
|
-
.
|
|
50
|
-
found
|
|
51
|
-
.iter()
|
|
52
|
-
.find(|rollout| rollout.session_id == session_id)
|
|
53
|
-
})
|
|
54
|
-
.map(|rollout| rollout.path.clone())
|
|
49
|
+
.and_then(|rollout| fs::canonicalize(&rollout.path).ok())
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
fn walk(dir: &Path, visit: &mut impl FnMut(&Path)) {
|
|
@@ -173,20 +168,43 @@ mod tests {
|
|
|
173
168
|
);
|
|
174
169
|
assert_eq!(
|
|
175
170
|
resolve("019b7000-0000-7000-8000-000000000002", &root),
|
|
176
|
-
Some(twin.
|
|
171
|
+
Some(fs::canonicalize(&twin).unwrap())
|
|
177
172
|
);
|
|
178
173
|
fs::remove_file(twin).unwrap();
|
|
179
|
-
assert_eq!(
|
|
180
|
-
resolve("019b7000-0000-7000-8000-000000000002", &root),
|
|
181
|
-
Some(twin_zst)
|
|
182
|
-
);
|
|
174
|
+
assert_eq!(resolve("019b7000-0000-7000-8000-000000000002", &root), None);
|
|
183
175
|
assert_eq!(
|
|
184
176
|
resolve("019b7000-0000-7000-8000-000000000001", &root),
|
|
185
|
-
Some(oldest)
|
|
177
|
+
Some(fs::canonicalize(oldest).unwrap())
|
|
186
178
|
);
|
|
187
179
|
assert_eq!(resolve("019b7000-0000-7000-8000-000000000099", &root), None);
|
|
188
180
|
assert_eq!(sessions_root(Some(&root)), root);
|
|
189
181
|
assert!(sessions_root(None).ends_with(".codex/sessions"));
|
|
190
182
|
fs::remove_dir_all(&root).ok();
|
|
191
183
|
}
|
|
184
|
+
|
|
185
|
+
#[cfg(unix)]
|
|
186
|
+
#[test]
|
|
187
|
+
fn resolve_returns_canonical_path_through_symlinked_root() {
|
|
188
|
+
let base = std::env::temp_dir().join(format!(
|
|
189
|
+
"codex-discovery-symlink-{}-{:?}",
|
|
190
|
+
std::process::id(),
|
|
191
|
+
std::thread::current().id()
|
|
192
|
+
));
|
|
193
|
+
fs::remove_dir_all(&base).ok();
|
|
194
|
+
fs::create_dir_all(&base).unwrap();
|
|
195
|
+
let root = base.join("sessions");
|
|
196
|
+
let rollout = touch(
|
|
197
|
+
&root,
|
|
198
|
+
"2026/01/02/rollout-2026-01-02T03-04-05-019b7000-0000-7000-8000-000000000003.jsonl",
|
|
199
|
+
);
|
|
200
|
+
let linked_root = base.join("linked-sessions");
|
|
201
|
+
std::os::unix::fs::symlink(&root, &linked_root).unwrap();
|
|
202
|
+
|
|
203
|
+
assert_eq!(
|
|
204
|
+
resolve("019b7000-0000-7000-8000-000000000003", &linked_root),
|
|
205
|
+
Some(fs::canonicalize(rollout).unwrap())
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
fs::remove_dir_all(&base).ok();
|
|
209
|
+
}
|
|
192
210
|
}
|
|
@@ -21,6 +21,27 @@ pub enum Lifecycle {
|
|
|
21
21
|
},
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
impl Lifecycle {
|
|
25
|
+
pub fn as_str(&self) -> &'static str {
|
|
26
|
+
match self {
|
|
27
|
+
Lifecycle::Uninitialized => "uninitialized",
|
|
28
|
+
Lifecycle::NoInstrumentation => "no_instrumentation",
|
|
29
|
+
Lifecycle::Open { .. } => "open",
|
|
30
|
+
Lifecycle::Completed { .. } => "completed",
|
|
31
|
+
Lifecycle::Aborted { .. } => "aborted",
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pub fn turn_id(&self) -> Option<&str> {
|
|
36
|
+
match self {
|
|
37
|
+
Lifecycle::Open { turn_id }
|
|
38
|
+
| Lifecycle::Completed { turn_id }
|
|
39
|
+
| Lifecycle::Aborted { turn_id, .. } => turn_id.as_deref(),
|
|
40
|
+
Lifecycle::Uninitialized | Lifecycle::NoInstrumentation => None,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
24
45
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
25
46
|
enum TurnState {
|
|
26
47
|
Open,
|
|
@@ -23,6 +23,15 @@ pub enum Provider {
|
|
|
23
23
|
Codex,
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
impl Provider {
|
|
27
|
+
pub fn as_str(self) -> &'static str {
|
|
28
|
+
match self {
|
|
29
|
+
Provider::Claude => "claude",
|
|
30
|
+
Provider::Codex => "codex",
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
#[derive(Debug)]
|
|
27
36
|
pub struct ParsedTranscript {
|
|
28
37
|
pub provider: Provider,
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
//! pyo3 exposure of the codex session surface: discovery, resolution, and the
|
|
2
|
+
//! per-rollout session-info fold the Python `cc_transcript.codex` facade
|
|
3
|
+
//! rehydrates. Paths cross as PathBuf, matching the CC discovery facade.
|
|
4
|
+
|
|
5
|
+
use std::path::PathBuf;
|
|
6
|
+
|
|
7
|
+
use pyo3::prelude::*;
|
|
8
|
+
use pyo3::types::{PyDict, PyList};
|
|
9
|
+
|
|
10
|
+
use cc_transcript_core::activity::PendingItem;
|
|
11
|
+
use cc_transcript_core::codex::{
|
|
12
|
+
self, lower, parse_codex_bytes, session_probe, CodexItem, CodexSession, CodexUsageAggregate,
|
|
13
|
+
Lifecycle,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
struct CodexInfo {
|
|
17
|
+
rollout_thread_id: Option<String>,
|
|
18
|
+
session_id: Option<String>,
|
|
19
|
+
parent_thread_id: Option<String>,
|
|
20
|
+
forked_from_id: Option<String>,
|
|
21
|
+
cwd: Option<String>,
|
|
22
|
+
originator: Option<String>,
|
|
23
|
+
cli_version: Option<String>,
|
|
24
|
+
model_provider: Option<String>,
|
|
25
|
+
lifecycle: Lifecycle,
|
|
26
|
+
pending: Vec<PendingItem>,
|
|
27
|
+
last_event_epoch: Option<i64>,
|
|
28
|
+
usage: CodexUsageAggregate,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fn session_meta_provider(session: &CodexSession) -> Option<String> {
|
|
32
|
+
session
|
|
33
|
+
.entries
|
|
34
|
+
.iter()
|
|
35
|
+
.find_map(|entry| match &entry.item {
|
|
36
|
+
CodexItem::SessionMeta(m) if m.id.as_deref().is_some_and(|id| !id.is_empty()) => {
|
|
37
|
+
Some(m)
|
|
38
|
+
}
|
|
39
|
+
_ => None,
|
|
40
|
+
})
|
|
41
|
+
.and_then(|m| m.model_provider.clone())
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn read_info(path: &str) -> std::io::Result<CodexInfo> {
|
|
45
|
+
let session = parse_codex_bytes(&std::fs::read(path)?);
|
|
46
|
+
let probe = session_probe(&session);
|
|
47
|
+
let usage = lower(&session).usage;
|
|
48
|
+
let model_provider = session_meta_provider(&session);
|
|
49
|
+
Ok(CodexInfo {
|
|
50
|
+
rollout_thread_id: session.rollout_thread_id,
|
|
51
|
+
session_id: session.session_id,
|
|
52
|
+
parent_thread_id: session.parent_thread_id,
|
|
53
|
+
forked_from_id: session.forked_from_id,
|
|
54
|
+
cwd: session.cwd,
|
|
55
|
+
originator: session.originator,
|
|
56
|
+
cli_version: session.cli_version,
|
|
57
|
+
model_provider,
|
|
58
|
+
lifecycle: probe.lifecycle,
|
|
59
|
+
pending: probe.pending,
|
|
60
|
+
last_event_epoch: probe.last_event_epoch,
|
|
61
|
+
usage,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn identity_dict<'py>(py: Python<'py>, info: &CodexInfo) -> PyResult<Bound<'py, PyDict>> {
|
|
66
|
+
let dict = PyDict::new(py);
|
|
67
|
+
dict.set_item("rollout_thread_id", info.rollout_thread_id.as_deref())?;
|
|
68
|
+
dict.set_item("session_id", info.session_id.as_deref())?;
|
|
69
|
+
dict.set_item("parent_thread_id", info.parent_thread_id.as_deref())?;
|
|
70
|
+
dict.set_item("forked_from_id", info.forked_from_id.as_deref())?;
|
|
71
|
+
dict.set_item("cwd", info.cwd.as_deref())?;
|
|
72
|
+
dict.set_item("originator", info.originator.as_deref())?;
|
|
73
|
+
dict.set_item("cli_version", info.cli_version.as_deref())?;
|
|
74
|
+
dict.set_item("model_provider", info.model_provider.as_deref())?;
|
|
75
|
+
Ok(dict)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn pending_list<'py>(py: Python<'py>, pending: &[PendingItem]) -> PyResult<Bound<'py, PyList>> {
|
|
79
|
+
PyList::new(
|
|
80
|
+
py,
|
|
81
|
+
pending
|
|
82
|
+
.iter()
|
|
83
|
+
.map(|item| {
|
|
84
|
+
let entry = PyDict::new(py);
|
|
85
|
+
entry.set_item("tool_use_id", item.tool_use_id.as_deref())?;
|
|
86
|
+
entry.set_item("name", &item.name)?;
|
|
87
|
+
entry.set_item("kind", item.kind.as_str())?;
|
|
88
|
+
Ok(entry)
|
|
89
|
+
})
|
|
90
|
+
.collect::<PyResult<Vec<_>>>()?,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn usage_dict<'py>(
|
|
95
|
+
py: Python<'py>,
|
|
96
|
+
usage: &CodexUsageAggregate,
|
|
97
|
+
) -> PyResult<Option<Bound<'py, PyDict>>> {
|
|
98
|
+
if usage.token_count_events == 0 {
|
|
99
|
+
return Ok(None);
|
|
100
|
+
}
|
|
101
|
+
let dict = PyDict::new(py);
|
|
102
|
+
dict.set_item("input_tokens", usage.input_tokens)?;
|
|
103
|
+
dict.set_item("cached_input_tokens", usage.cached_input_tokens)?;
|
|
104
|
+
dict.set_item("output_tokens", usage.output_tokens)?;
|
|
105
|
+
dict.set_item("reasoning_output_tokens", usage.reasoning_output_tokens)?;
|
|
106
|
+
dict.set_item("total_tokens", usage.total_tokens)?;
|
|
107
|
+
dict.set_item("model_context_window", usage.model_context_window)?;
|
|
108
|
+
dict.set_item("token_count_events", usage.token_count_events)?;
|
|
109
|
+
Ok(Some(dict))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#[pyo3_stub_gen::derive::gen_stub_pyfunction]
|
|
113
|
+
#[pyfunction]
|
|
114
|
+
#[gen_stub(override_return_type(type_repr = "dict[str, typing.Any]", imports = ("typing",)))]
|
|
115
|
+
pub fn codex_session_info<'py>(py: Python<'py>, path: String) -> PyResult<Bound<'py, PyDict>> {
|
|
116
|
+
let info = py.detach(|| read_info(&path))?;
|
|
117
|
+
let out = PyDict::new(py);
|
|
118
|
+
out.set_item("identity", identity_dict(py, &info)?)?;
|
|
119
|
+
let lifecycle = PyDict::new(py);
|
|
120
|
+
lifecycle.set_item("state", info.lifecycle.as_str())?;
|
|
121
|
+
lifecycle.set_item("turn_id", info.lifecycle.turn_id())?;
|
|
122
|
+
out.set_item("lifecycle", lifecycle)?;
|
|
123
|
+
out.set_item("pending", pending_list(py, &info.pending)?)?;
|
|
124
|
+
out.set_item("last_event_epoch", info.last_event_epoch)?;
|
|
125
|
+
out.set_item("usage", usage_dict(py, &info.usage)?)?;
|
|
126
|
+
Ok(out)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#[pyo3_stub_gen::derive::gen_stub_pyfunction]
|
|
130
|
+
#[pyfunction]
|
|
131
|
+
#[pyo3(signature = (root=None))]
|
|
132
|
+
pub fn codex_discover(root: Option<PathBuf>) -> Vec<(PathBuf, String, bool)> {
|
|
133
|
+
codex::discover(&codex::sessions_root(root.as_deref()))
|
|
134
|
+
.into_iter()
|
|
135
|
+
.map(|rollout| (rollout.path, rollout.session_id, rollout.compressed))
|
|
136
|
+
.collect()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#[pyo3_stub_gen::derive::gen_stub_pyfunction]
|
|
140
|
+
#[pyfunction]
|
|
141
|
+
#[pyo3(signature = (session_id, root=None))]
|
|
142
|
+
pub fn codex_resolve(session_id: &str, root: Option<PathBuf>) -> Option<PathBuf> {
|
|
143
|
+
codex::resolve(session_id, &codex::sessions_root(root.as_deref()))
|
|
144
|
+
}
|