Skip to content

feat: bound gateway requests, stdin sends, and normalized transcripts - #20

Open
ImL1s wants to merge 15 commits into
ScriptedAlchemy:mainfrom
ImL1s:feat/bounded-gateway-and-stdin-sends
Open

feat: bound gateway requests, stdin sends, and normalized transcripts#20
ImL1s wants to merge 15 commits into
ScriptedAlchemy:mainfrom
ImL1s:feat/bounded-gateway-and-stdin-sends

Conversation

@ImL1s

@ImL1s ImL1s commented Sep 9, 2026

Copy link
Copy Markdown

Three related changes that came out of driving gbot from scripts and cron rather than by hand. Each is independent in the diff, so I'm happy to split this into separate PRs if you'd prefer to review them apart.

1. Bound gateway requests and stop leaking response bodies

ensureSandbox / gatewayCall used bare fetch with no deadline, so a hung gateway hung the CLI indefinitely, and readJson folded an unparseable body into { raw: text } which then went into the thrown error message.

  • All gateway requests now run under a deadline (15s default) that covers reading the response body, not just headers — implemented with AbortController + Promise.race so a stalled body read can't outlive it. ensureSandbox and gatewayCall accept an optional positive timeoutMs in their final options argument.
  • redirect: "error" — a redirected gateway call fails instead of silently following.
  • Errors now report only method and status. No server response body, no credentials.
  • Ambiguous sends are labelled, not retried. A sendPrompt that hits a timeout, a network failure, HTTP 408/5xx, or an invalid/empty success response throws with effect: "unknown" and a message saying delivery is unknown and not to resend automatically. Nothing is retried for you, and a fresh CLI send uses a new client nonce, so re-invoking is not a deduplicated retry — the README says to read the thread back and verify first.
  • Plain-text transcripts now render nested message.content / message.text carriers, which previously printed as empty lines.

2. send <target> --stdin

Passing a message as an argument puts it in the process argument list, where anything reading ps can see it, and shell quoting mangles exact UTF-8. --stdin reads the message from standard input instead, validated strictly: non-empty, valid UTF-8, no NUL bytes, no surrounding whitespace, at most 64 KiB, and not combined with a positional message. The README notes using printf %s rather than echo when a trailing newline isn't intended.

3. A normalized transcript contract for adapters

--json thread / --json chat returned the raw gateway response, whose shape varies, so every integration wrote its own defensive parser. --normalized returns a stable shape: the target identity plus messages with id, an explicit user / assistant / unknown role, and text.

The normalizer fails closed rather than guessing — malformed containers, malformed target/message identities, and conflicting or malformed ID carriers are rejected instead of being coerced into something plausible. Normalized text is kept separate from display stringification, so non-text content is never synthesized into JSON that looks like a message body. Without --normalized, JSON output is unchanged.

Testing

  • node --test71 tests, 0 failures (674 new lines of tests: test/gateway.test.js covers the deadline, redirect rejection, and each ambiguous-send classification; test/send-stdin.test.js covers the stdin validation matrix; test/transcript.test.js covers the normalizer's fail-closed paths)
  • npx publint@0.3.24 — All good
  • npm pack + global install + gbot --help smoke test — passes
  • Verified against a live signed-in Grok Bot macOS app: bots list, thread, --json thread --normalized, and send --stdin

Changesets

Two included: a patch for the gateway/transcript fix and a minor for the --stdin flag and --normalized contract.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 26c54c5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
grok-bot-cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T04:56:56.493396Z f7990a4 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4fbcb19a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js
When the gateway returns a non-OK status but its body stream stalls,
requestJson previously raced readJson against the deadline, causing
the known HTTP error to be misclassified as GATEWAY_TIMEOUT with
unknown delivery effect.

Now requestJson checks res.ok immediately after the fetch resolves
and returns before attempting to read the body. This ensures client
errors (4xx) and server errors (5xx) are reported with their actual
status codes even when the response body never completes.

Addresses Codex review feedback on PR ScriptedAlchemy#20.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: edd6a36b9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js Outdated
Comment thread src/cli.js Outdated
Comment thread src/transcript.js Outdated
Three independent fixes addressing all remaining P2 suggestions from
the Codex automated review on PR ScriptedAlchemy#20:

1. Treat JSON null sendPrompt responses as unknown delivery — null
   provides no confirmation the prompt was accepted, so it follows
   the same path as empty or non-JSON success responses.

2. Preserve leading UTF-8 BOM during stdin decoding (ignoreBOM: true)
   so the whitespace check rejects it rather than silently stripping
   the first character of the message.

3. Include entry.sender in the normalizer's role candidate set so
   transcripts with an explicit sender field produce the correct
   user/assistant role instead of unknown.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55302127a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/transcript.js
When a transcript entry carries an explicit but unsupported role
string (e.g. role: 'system' or sender: 'system') alongside a
user/assistant carrier from a nested field, the normalizer now
returns 'unknown' instead of letting the nested carrier win.

This only applies to strict role fields (role, sender, nested.role,
nested.type). Loose category carriers like entry.kind ('send-message')
and entry.type ('text') continue to contribute user/assistant roles
without vetoing, since they are multi-purpose identifiers.

Addresses Codex review round 3 on PR ScriptedAlchemy#20.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a041a86e6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js Outdated
Comment thread src/gateway.js Outdated
Two fixes from Codex review round 4:

1. Abort the controller when returning early on non-OK HTTP status.
   Without this, the response body stream and underlying socket
   remain active indefinitely, potentially exhausting the connection
   pool on repeated failures.

2. Treat JSON false sendPrompt responses as unknown delivery.
   A boolean cannot confirm prompt acceptance, so it follows the
   same unknown-delivery path as null and empty body responses.
   Non-send methods continue to pass false through unchanged.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ba2e655c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js Outdated
Comment thread src/transcript.js Outdated
…iers

Two fixes from Codex review round 5:

1. Require sendPrompt data to be a non-null object for a successful
   acknowledgement. Any non-object JSON value (null, false, 0, '')
   is treated as unknown delivery. This replaces the growing list of
   individual falsy-value checks with a positive shape assertion.

2. Malformed non-string strict role carriers (e.g. role: 42, role:
   true) now veto role inference the same way unsupported string
   roles do. Empty strings are treated as absent (no veto).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f6533c79b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js Outdated
Comment thread src/transcript.js
Two fixes from Codex review round 6:

1. Exclude arrays from valid sendPrompt acknowledgements. Since
   typeof [] === 'object', the previous positive shape check
   still accepted empty arrays. Added Array.isArray guard.

2. Reject non-string, non-object text carrier values (e.g. text: 42)
   as malformed evidence instead of silently filtering them out and
   falling back to secondary carriers. This preserves the fail-closed
   normalizer contract.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cecb43b3e1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/transcript.js
When content arrays contain unsupported primitives (e.g. ["hello", 42]),
the array branch silently filters them out. Validate each element and
reject non-string, non-object primitives to preserve fail-closed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b906bb8dd6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js Outdated
Comment thread src/transcript.js
- sendPrompt now requires response to have at least one key
- Strict text carriers (text, prompt, preview) with opaque objects
  (no TEXT_CARRIERS subkeys) are rejected as malformed evidence
- content/message carriers continue to accept rich objects like
  { type: 'image' }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31b03adbda

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/gateway.js Outdated
Extract sendPrompt validation into isAffirmativeSendAck helper.
Reject responses with ok:false, success:false, or an error field
since they explicitly indicate the gateway rejected the prompt
despite returning a 2xx status code.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26c54c5b33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli.js
Comment thread src/gateway.js Outdated
When ok or success fields are present but not exactly boolean true
(e.g. ok:'false', success:[], ok:0), reject the response as unknown
delivery instead of trusting truthy coercion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant