Repository navigation
0.5.0 (4/5): telemetry switches, dg init, JSON output and listen output formats - #163
Closed
dg-coreylweathers wants to merge 13 commits into
Closed
dg-coreylweathers wants to merge 13 commits into
dg-coreylweathers wants to merge 13 commits into
Conversation
Error reports now also stay off when DO_NOT_TRACK or CI is set, with the same rule as DEEPCTL_TELEMETRY_DISABLED: any non-empty value except 0, false, no or off. They are checked after the dg-specific switch, and the help footer then reads "Telemetry is off.". The README's opt-out list and the help footer name both. Both variables join the root --help cache key, since the footer depends on them. Telemetry's own tests clear both variables, so they behave the same on a CI runner.
When the report is dropped before it is queued (a closed-stream error, or nothing left to send), there is nothing to wait for, so the flush is skipped. Tests pin the two timing cases: a refused connection ends the report at once, and an unanswered one is held to the budget.
With DEEPCTL_TELEMETRY=log, an unexpected error prints the report dg would send to stderr as one JSON line, and nothing is sent: the SDK gets a transport that only writes to stderr and no destination. It changes nothing when telemetry is off. DEEPCTL_TELEMETRY=off (or 0, false, no, disabled) turns telemetry off, like DEEPCTL_TELEMETRY_DISABLED. Any other value keeps the default. The variable joins the root --help cache key.
docs/telemetry.md lists every field a report can hold, the opt-outs in the order they are checked, the time budget and the log mode. A test sends a report through the real error path (an exception chain, with the command context filled in) and checks that its fields are exactly the documented ones. A missing doc fails the test.
The template service supplies both the repository URL and the template name, and dg init used them as given. It now clones only https://github.com/deepgram/<repo> and https://github.com/deepgram-starters/<repo>: no other host, scheme, port, user name, query, fragment, extra path segment, whitespace or control character. Both the repository name and the template name must be plain: ASCII letters, digits, ".", "_" and "-", not starting with "-". Anything else is an error before the name is used in a path, a command or output, and nothing is cloned.
Every git command dg init runs now goes through one helper that adds --no-pager and -c core.fsmonitor=false, core.hooksPath=/dev/null (NUL on Windows), core.pager=cat and diff.external= to the command line, so a repository's own configuration can't name a program for git to run. The commands dg runs inside a repository (rev-parse, ls-files and check-ignore) apply no filters and open no connection. The .env check also skips the global excludes file, which as a FIFO would block it. Inherited GIT_* variables are still dropped for commands in a repository; the clone keeps them, as before.
dg init failed on any existing directory. It now records each clone it makes in its own state directory (~/.deepctl/init-clones, 0700): one owner-only file per clone, named after the directory's real path, holding that path, the template, its repository, the commit cloned, the phases done and the directory's device and inode. The file is written atomically and never through a link. A rerun into that directory resumes from the record, without cloning again or repeating finished setup, only when all of these hold: - the record names this real path and the template's repository; - the directory, opened without following a link, has the recorded device and inode, so one deleted and made again at the path doesn't count, and it and its .git (a real directory) belong to this user (on Windows, where inodes and owners aren't reliable, only the link and .git checks apply); - git then finds it the top of its own work tree, at the recorded commit. Nothing inside the directory is trusted for this, and no git command runs in it before the checks above. Any other existing directory, however it looks, is invalid_argument: no .env is written and no setup or start step runs. An empty directory can be cloned into.
An AI agent session, CI or a run without a terminal no longer runs a
template's setup commands unasked, and never starts the app on its own.
- New -y/--yes approves the setup commands. Without an AI agent, an
explicit --install approves them too; in an agent session only --yes
does. Otherwise init returns confirmation_required (exit 1) with
details.flag --yes and a next_step that finishes without them:
`{prog} init {template} --dir {dest} --no-install --no-start`.
- Commands the template service lists are confirmed before anything is
cloned, and the refusal names exactly what --yes runs: the listed
commands, or make check-prereqs and make init if the repository has
a Makefile (whose targets take precedence, as before). A Makefile
with no listed commands is confirmed after the clone; next_step then
resumes in it.
- The start step runs only with --start; otherwise it is
confirmation_required with next_step `... --no-start`.
A person at a terminal keeps the existing prompts.
Machine JSON on a pipe or file went through rich's print_json, which re-parses the text and renders it as highlighted segments: 11 s for a 14 MB result. Off a terminal, write the same text rich printed (json.dumps with indent 2 and ensure_ascii off, plus a newline) to the console's stream in one write. A terminal still gets highlighting, and --quiet still prints nothing.
The transport tests' fixture swapped the shared httpx client out with monkeypatch and never closed the one a test had built, so a keep-alive connection to that test's fake server stayed open until the collector found it. It then raised a ResourceWarning inside whichever later test happened to be running, which failed test_find_available_port_closes_sockets in debug-browser. Close the shared and verified clients a test made, and restore the ones before.
Prerecorded listen under -o json (asked for or implied) now posts through the shared HTTP client and prints the response body as the server sent it, decoded, plus a newline. The SDK model round trip (construct_type, model_dump, re-serializing) is gone from this path: a 50k-word result takes about 0.3 s instead of 10 s. The body is printed only after it was read completely with a 2xx status and parsed as a JSON object; the bytes printed are the ones read. A 2xx body that isn't a JSON object (a portal's HTML page, a stream cut short, invalid UTF-8, an array) is now an error, network_unavailable with exit 1, as are a coding dg can't decode, a corrupt one, and a body cut short in transfer. Error statuses map to the usual coded errors, so stdout holds the whole body or the error envelope, never part of a body. Requests retry as the SDK does, before any response byte is read, and identify themselves with the SDK's User-Agent and X-Fern headers. zstd is accepted only where it can be decoded. Keys the server didn't send no longer appear as null, and dates keep the server's form. DEEPCTL_LISTEN_JSON=sdk prints the SDK's shape as before; any other value is ignored with one warning. -o yaml now prints the body as YAML instead of the table. Other backends keep the SDK path.
dg listen -o text prints only the transcript (speaker-labelled with --diarize), for files, URLs and live streams. It isn't a machine format: errors stay on stderr. A command can now declare output formats it takes after its name; the root -o is unchanged. Help specs regenerated.
When an AI agent marked the environment, the format was chosen by detection rather than asked for, stdout is neither a regular file nor /dev/null, and the prerecorded result is over 64 KiB, dg listen saves the full result and the plain transcript to a pair of 0600 files and prints a summary instead: their paths, sizes, a 2 KiB preview, the request ID and duration. One stderr line names the file, even under --quiet. --full-output, on listen or the root, prints it all. Files go in ~/.deepctl/results/ (0700), else in a private per-user directory under the temp directory; a planted or shared one is refused with a warning and a fresh one is used. They are created exclusively, opened without following links and fsynced before stdout is written. Pairs older than a day, then the oldest past 500 MB, are pruned. When neither place can be written, the run fails with state_unwritable and never prints the full result. Help specs regenerated.
dg-coreylweathers
requested review from
GregHolmes and
deepgram-kiley
as code owners
November 7, 2026 02:35
Contributor
Author
|
Consolidated into #165, which has all 74 commits of 0.5.0 in one PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 4 of 5 of the 0.5.0 release, stacked on part 3. Review in order. Merge only after the PR below it has merged, and only after 0.4.1 is released.
Telemetry switches (
DO_NOT_TRACK,CI,DEEPCTL_TELEMETRY=log|off) are accepted; this version still sends no telemetry.dg initclones only Deepgram templates, keeps repository settings from running programs, can resume, and needs--yesfor setup steps outside a terminal. JSON output off a terminal skips rich.dg listenprints the server body for-o json, adds-o text, and saves large results to a file in agent sessions.Commits
Notes
🤖 Generated with Claude Code