Member Junction
    Preparing search index...

    Shared driver for harnesses that expose themselves as a CLI emitting newline-delimited JSON.

    That covers most of the ecosystem — Codex, OpenCode, Gemini CLI and Pi all work this way, and only Claude Code ships a real TypeScript SDK. Rather than repeat process management, line framing and lifecycle in four adapters, subclasses supply just the two things that genuinely differ: BuildTurnArgs (how to phrase this turn on the command line) and MapEvent (what the vendor's JSON means).

    These CLIs are built to be invoked, do a unit of work, and exit — continuity comes from a session id passed back on the next invocation, not from a long-lived process. Modelling it that way keeps the adapter honest about what actually persists: if the harness cannot resume (CapabilitySettings.SessionResume false) the subclass must replay context into the next invocation itself, and report the extra tokens through a usage event so the run's cost guardrail sees the true spend rather than a flattering one.

    Every process goes through config.Executor, never spawn directly. That is what lets the same adapter run on a developer's laptop and inside a per-run container without knowing the difference, and it is what stops a production deployment from executing an autonomous agent's shell commands inside the MJAPI container while the agent's config claims provider: 'docker'.

    A non-zero exit or unparseable stream yields a session-error event rather than a throw, because the caller's accumulation loop needs exactly one terminal event per turn to make progress. A throw from inside the async iterator would strand the run between turns with no recorded reason.

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    activeProcess: HarnessProcess = null
    config: HarnessSessionConfig = null
    sessionId: string = undefined

    Accessors

    • get DidResumeSession(): boolean

      Whether this session actually continued HarnessSessionConfig.ResumeSessionId.

      Default false: a harness that cannot resume, or one offered no prior session, starts cold and must be sent the full conversation. Adapters that DO resume MUST override this and report truthfully — the caller sends only the newest message when it returns true, so a false positive leaves the harness answering a question it never saw the context for.

      Deliberately separate from CapabilitySettings.SessionResume. That flag says the adapter CAN resume in principle; this says it DID, this time. A stale or pruned session id makes the two disagree, and only the second one is safe to branch the turn input on.

      Returns boolean

    • get ReportedModel(): string

      The model the harness actually used, if it reports one.

      Adapters that can observe this SHOULD override it. Accounting resolves AIPromptRun.ModelID from this first and only falls back to the harness row's declared model, because a harness left to choose its own model will — and billing a run against a model it never used is worse than having no attribution at all, since it looks authoritative.

      Returns string

    Methods

    • Translates MJ's permission policy into whatever this harness understands.

      Overridable and default no-op, matching the other capability seams. An adapter that cannot enforce permissions should leave this alone AND report PermissionHooks: false, so the runtime knows the policy is advisory rather than enforced. Silently accepting a policy you cannot apply is the worst option: the operator believes strict is gating something.

      Called once, before StartSession, so adapters can fold the result into launch flags.

      Parameters

      Returns void

    • Builds the argv for one turn.

      Parameters

      • input: string

        Task prompt on the first turn; formatted step results afterwards.

      • isFirstTurn: boolean

        Lets a subclass choose between "start" and "resume" argument forms.

      Returns string[]

    • Tears the session down.

      MUST be idempotent and MUST be safe to call on every exit path — success, failure, cancellation and crash — because it is what revokes the per-run MCP credential and releases the workspace. A teardown that only runs on the happy path leaks a live credential.

      Returns Promise<void>

    • Answers a permission-request the adapter raised.

      Only meaningful when Capabilities.PermissionHooks is true; adapters without hooks should treat this as a no-op rather than throwing, because the posture layer above may still call it defensively.

      Parameters

      • _requestId: string
      • _approved: boolean
      • Optional_note: string

      Returns Promise<void>

    • Runs one turn and streams what happens.

      The first call receives the task prompt; subsequent calls receive formatted results of steps MJ executed on the harness's behalf. Implementations MUST emit exactly one terminal event — turn-complete or session-error — so the caller's accumulation loop always terminates.

      Where the harness cannot resume a session natively (SessionResume false), the adapter is responsible for replaying prior context into a fresh invocation here, and for reporting the resulting token cost through usage so the run's guardrails see the true spend.

      Parameters

      • input: string

      Returns AsyncIterable<HarnessTurnEvent>

    • Supplies MJ's system prompt for the session, where the harness can accept one.

      Harnesses ship their own system prompt defining their identity, and it dominates anything sent as user text. MJ's turn-end contract delivered as a user message therefore competes with the harness's own instructions and loses — observed directly: a harness given the contract in the user turn still answered "what can you do?" in prose, costing a retry every run.

      Adapters whose harness accepts a system prompt SHOULD override this. Those that cannot are no worse off than before: the contract still rides in the turn input.

      Parameters

      • _systemPrompt: string

      Returns void

    • Starts one turn's process INSIDE THE SANDBOX.

      Note what this does not do: call spawn. Where the process physically runs is the sandbox provider's decision, delivered through the executor, so this adapter behaves identically on a laptop and in a per-run container.

      Parameters

      • input: string
      • isFirstTurn: boolean

      Returns HarnessProcess