powered by
etapx

0%

(
July 28, 2026
)

Why Influxx Splits Large TypeScript Modules Into Provider Tables

How a hard, never-disabled line-count cap forced a 400-line session detector into a declarative provider table plus one dedicated module.
Why Influxx Splits Large TypeScript Modules Into Provider Tables
Why Influxx Splits Large TypeScript Modules Into Provider Tables
How a hard, never-disabled line-count cap forced a 400-line session detector into a declarative provider table plus one dedicated module.

Influxx's Native Chat has to answer a deceptively hard question for every open worktree: which on-disk agent session is the live one right now? The module that answers it, discover-active-session.ts, used to be one long function that handled Grok, Claude, Codex, and Kimi inline, each with its own file layout and matching rules stacked into the same body. As part of Influxx 1.2 it's been split into a small dispatcher, a declarative provider-source table (discover-active-session-sources.ts), and a dedicated module for the one provider whose detection logic never fit the general pattern (kimi-active-session-discovery.ts). This isn't a user-facing feature. It's a maintainability decision, and it's one Influxx enforces as policy rather than leaving to individual judgment.

What "Discover the Active Session" Actually Has to Do

Every supported CLI stores its session transcripts in a different place with a different key for matching them to a working directory. Grok keeps an active_sessions.json that Influxx reads and matches against the target cwd. Claude, OpenClaude, GLM, and Agent Teams all write into a ~/.claude/projects/<cwd-slug>/ directory keyed by an encoded path, and Influxx picks the newest .jsonl file by modification time. Codex nests rollouts under date directories and requires opening each candidate file's head bytes to read a session_meta record before its cwd field can even be compared. Kimi is scoped by a separate session_index.jsonl that maps session ids to working directories, which then has to be cross-referenced against each session's state.json.

Four providers, four completely different matching strategies, one shared question: which session id and transcript path should Influxx attach to this pane right now.

The Line-Count Cap That's Never Disabled

Influxx's engineering guidelines include a rule that sounds almost too simple to matter: never disable the max-lines lint rule, and never bump the per-file line limit as a workaround. No eslint-disable max-lines, no oxlint-disable max-lines, no line-specific exception, and no quiet increase to the limit itself. That rule exists precisely because a single large function handling every provider's special case is the natural failure mode of exactly this kind of code — each new CLI adds a few more lines of provider-specific branching to the same function, and nothing ever forces the accumulation to stop.

"The line cap isn't there because long files are ugly. It's there because it's the only enforcement mechanism that reliably fires before a function like this one turns into an unreadable pile of provider-specific if-branches. Every time discover-active-session hit the limit, that was the signal to actually split the responsibility instead of finding a way around the rule."

— Halvard Reyes, Senior Engineer, Developer Platform at ETAPX

The discipline is in refusing the workaround. It would be trivial to bump the limit, or to disable the check for one file with a comment explaining why this file is special. Influxx's policy treats that exception request as the actual problem to fix, not something to silence — the rule catches the file, and the response has to be decomposition, not permission.

A Declarative Provider Table Instead of One Long Function

The result is discover-active-session-sources.ts, which exports one function per provider family — discoverGrokActiveSession, discoverClaudeCompatibleActiveSession, and discoverCodexActiveSession — each self-contained, each responsible only for its own provider's file layout and matching rules. discover-active-session.ts itself shrinks down to a thin dispatcher: it normalizes the incoming options, builds a shared isSessionAllowed guard (which rejects sessions already excluded from a previous conversation, or written before a "new chat" cutoff with a two-second mtime grace window for filesystem resolution), and then routes to whichever provider function matches the requested agent.

That dispatcher still has to handle real nuance — Agent Teams and GLM are Claude Code under the hood, so both route to the same ~/.claude/projects JSONL lookup as claude itself — but that nuance is now a few lines of routing logic, not a few hundred lines of interleaved provider branching. Each provider function can be read, tested, and changed on its own, without scrolling past three other providers' logic to find the part that's actually relevant.

Isolating each provider also made its performance characteristics visible instead of buried. Codex's function alone has to sort every candidate rollout by modification time and then read only the first 8KB of each file's head — capped at 48 candidates by default — to find the session_meta record without opening and parsing an entire transcript just to check its working directory. That kind of provider-specific tuning is easy to reason about when it lives in a dedicated discoverCodexActiveSession function; it would have been one more tangle of constants sitting inside a single do-everything function otherwise.

The One Provider That Didn't Fit the Table

Kimi is the exception, and the codebase is honest about that rather than forcing it into the same shape as the other three. Kimi sessions are scoped by working directory through a separate session_index.jsonl file, which has to be resolved per candidate before Influxx even knows whether a given state.json is relevant. Kimi also determines its actual transcript path — the wire.jsonl for the primary agent — from state that lives one level removed from the session file being scanned, and it walks every state.json under the sessions directory rather than matching a single index the way Grok does. That's meaningfully different plumbing from the other three providers, so it lives in its own file, kimi-active-session-discovery.ts, imported by the dispatcher exactly like the table-driven providers are.

"Part of why I trust a tool for my day job is being able to see how the team actually writes code, not just the marketing page. Reading through how they split up session detection instead of cramming every provider into one function told me more about how this codebase will age than any feature list would have."

— Owen Prescott, platform engineer at Cascadia DevOps Collective, Influxx user

Naming as Documentation

Influxx's naming conventions push in the same direction as the line cap. Files aren't allowed to be named after their generic role — no helpers.ts, utils.ts, or common.ts — specifically because those names carry no information about what's actually inside and tend to accumulate unrelated logic over time. discover-active-session-sources.ts and kimi-active-session-discovery.ts both name the concrete domain concept they own rather than a vague role. A developer opening the file tree can guess correctly what each file does before opening it, which matters more as the number of providers — and the number of files like this one — keeps growing.

What This Refactor Did Not Change

It's worth being direct about what this work is and isn't. The public entry point, discoverActiveNativeChatSession, keeps the same signature and the same behavior for every caller — Native Chat's pane-attachment logic doesn't know or care that the internals were reorganized. There's no new capability here, no new provider support, no change to what a user sees. The session-exclusion and "not before" guards that prevent a stale transcript from reattaching after a developer starts a fresh conversation still apply identically across all four providers, funneled through the same shared isSessionAllowed check regardless of which provider function calls it. The value is entirely in what happens the next time a provider needs new matching logic, or the next time an engineer has to debug why a session didn't attach: the change lands in one focused, well-named file instead of threading a needle inside a function that also has to stay correct for three other providers at the same time.

"Refactors like this one don't show up in a changelog a user reads, and that's fine — they're not supposed to. What they buy us is the ability to keep adding CLI support at the pace we have been without the codebase getting harder to work in every time we do it. That's a compounding return, even if it's invisible from the outside."

— Marcus Whitfield, VP of Engineering at ETAPX

Frequently Asked Questions

Does this refactor change how Influxx behaves for end users?

No. The public function that other code calls to discover an active session kept the same name, signature, and behavior — this is an internal reorganization, not a feature change.

Why does Kimi get its own file instead of joining the provider table?

Kimi's session-to-workdir matching goes through a separate index file and a nested state file in a way the other three providers' lookups don't, so folding it into the same declarative table would have made the table itself less readable rather than more.

What actually enforces the line-count limit — is it just a guideline?

It's a lint rule, and Influxx's engineering policy explicitly forbids disabling it or bumping a file's limit as a workaround, including file-specific and line-specific exceptions.

Could a new provider still make one of these files grow too large again?

Yes, in principle — the cap doesn't prevent that on its own. What it does is guarantee the growth gets caught and forces a decomposition decision at that point, rather than allowing the file to silently keep growing past what's readable.

Is this kind of internal refactor something Influxx does regularly?

The line-count and naming rules are standing policy, not a one-time cleanup, so this pattern — split when a file grows past the cap, name files after what they concretely contain — applies across the codebase, not just to this one module.

Does splitting a function into multiple files hurt performance?

No. This is a source-level organizational change; the dispatcher still calls the same underlying logic, just from separate, more focused files rather than one large one.

Why should a developer evaluating Influxx care about an internal refactor like this?

It's a signal of how the codebase is likely to hold up as more CLIs and features get added — a team that treats file size and naming as enforced policy rather than aspiration tends to produce code that stays debuggable as it grows.

Nobody using Influxx will ever see this file, which is exactly why the discipline to keep it small had to be enforced rather than hoped for.