Skip to content

feat(integrations): run catalog tools with local credentials - #3843

Merged
kojiwakayama merged 26 commits into
mainfrom
feat/issue-495-byo-local-integration-credentials
Aug 19, 2026
Merged

feat(integrations): run catalog tools with local credentials#3843
kojiwakayama merged 26 commits into
mainfrom
feat/issue-495-byo-local-integration-credentials

Conversation

@kojiwakayama

@kojiwakayama kojiwakayama commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Description

Add an explicit catalog-backed integration source for local and self-hosted projects. Callers grant exact canonical tool IDs, resolve project-owned credentials by name at execution time, and call supported fixed HTTPS REST endpoints without a Veryfront account or project token.

The boundary supports header API keys, HTTP Basic, OAuth 2.0 client credentials, and the Salesforce service-account specialization. Unsupported authentication, endpoint, response, runtime, and catalog contracts fail closed. Credential values stay out of tool metadata, arguments, URLs, logs, errors, caches, and Veryfront APIs. The existing managed remote integration path is unchanged.

Documentation now presents the local path as first-class and distinguishes it from managed OAuth and remote execution.

Related Issue(s)

Completes the BYO integration-credentials ask in https://github.com/veryfront/veryfront-issue-inbox/issues/495

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test update

Verification

  • RED/GREEN regressions cover exact grants, credential-name admission, pre-auth argument validation, 204 responses, provider egress, secret isolation, and hosted compatibility.
  • Complete pre-push suite: 3,945 passed, 30,343 steps, 0 failed.
  • Focused integration/tool matrix: 38 tests, 243 steps, 0 failed.
  • Deno check, lint, fmt, anti-slop, and diff checks pass.
  • CI-pinned Deno 2.7.7 test-typecheck baseline: 48 grandfathered, 0 new.
  • CI-pinned API reference check: current (44 files).
  • Docs validation: 1,385 links, 0 failures.
  • Full npm build completed.
  • Independent Spec, Standards, and credential-boundary reviews approve exact HEAD.

Checklist

  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works

Summary by CodeRabbit

  • New Features

    • Added account-free local integration tools using project-owned credentials.
    • Supports API keys, Basic authentication, OAuth client credentials, and Salesforce service accounts.
    • Added explicit tool grants, secure credential handling, bounded HTTPS execution, and structured results.
    • Added clearer local integration error reporting with remediation guidance.
  • Documentation

    • Expanded integration, Salesforce, self-hosting, architecture, support, and API documentation.
  • Tests

    • Added comprehensive coverage for authentication, execution safeguards, credential isolation, Salesforce flows, errors, and documentation examples.

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

📦 Client bundle boundary

Entrypoint Modules Source size Server leaks
src/index.client.ts 326 1944 KiB ✅ 0

A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in scripts/lint/client-bundle-baseline.json to burn down.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kojiwakayama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: da1febe3-69bf-4a11-a258-4ddb4009086c

📥 Commits

Reviewing files that changed from the base of the PR and between 85cad0e and 54c379f.

📒 Files selected for processing (4)
  • docs/api-reference/veryfront/integrations.md
  • src/integrations/local-endpoint-executor.ts
  • src/integrations/local-tool-source.test.ts
  • src/integrations/local-tool-source.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8fab4d7a-6e50-4624-b83c-0b8fe982ed89

📥 Commits

Reviewing files that changed from the base of the PR and between c26c725 and 85cad0e.

⛔ Files ignored due to path filters (1)
  • templates/manifest.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (6)
  • docs/api-reference/veryfront/integrations.md
  • src/integrations/local-endpoint-executor.test.ts
  • src/integrations/local-endpoint-executor.ts
  • src/integrations/local-integration-errors.ts
  • src/integrations/local-tool-source.test.ts
  • src/integrations/local-tool-source.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/api-reference/veryfront/integrations.md
  • src/integrations/local-tool-source.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

This change adds account-free local integration tools. The implementation validates catalog grants, resolves project credentials, executes guarded provider REST requests, exposes typed errors, and documents local, Salesforce, self-hosted, and remote execution boundaries.

Changes

Local integration runtime

Layer / File(s) Summary
Credential authentication and error contracts
src/integrations/local-credential-auth.ts, src/integrations/local-integration-errors.ts, src/errors/error-registry/*, src/integrations/limits.ts, src/integrations/index.ts, src/integrations/source-policy.ts, src/integrations/local-credential-auth.test.ts, src/errors/error-registry.test.ts
Adds API-key, Basic, OAuth client-credentials, and Salesforce service-account authentication. Adds credential validation, secret-safe diagnostics, limits, public exports, and six registered errors.
Guarded local endpoint execution
src/integrations/local-endpoint-executor.ts, src/integrations/local-endpoint-executor.test.ts
Adds bounded request construction, exact-origin enforcement, timeout and cancellation handling, response-size limits, JSON parsing, response transforms, and transport tests.
Catalog-backed local tool source
src/integrations/local-tool-source.ts, src/integrations/local-tool-source.test.ts
Adds explicit canonical tool grants, immutable catalog metadata, sanitized schemas, runtime restrictions, credential injection, Salesforce origin substitution, and authenticated execution.
Documentation and contract coverage
docs/api-reference/veryfront/integrations.md, docs/architecture/*, docs/guides/**, docs/superpowers/**, src/README.md, tests/docs/*
Documents local and remote execution, Salesforce service accounts, self-hosting support boundaries, local errors, design requirements, and guide contracts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Merge Risk: 🟡 Moderate · up to 85cad

This PR adds local credential-backed catalog execution, but malformed credentials may cause opaque request failures, catalog validation may prevent unrelated integrations from importing, and part of the test suite may be skipped by Node and Bun; documentation also currently describes the capability inaccurately. Merge should wait for these bounded risks to be fixed or explicitly accepted.

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant LocalToolSource
  participant CredentialProvider
  participant Provider
  Agent->>LocalToolSource: load exact-grant tools
  LocalToolSource->>CredentialProvider: resolve named credential
  CredentialProvider-->>LocalToolSource: return credential value
  LocalToolSource->>Provider: execute guarded authenticated request
  Provider-->>LocalToolSource: return provider response
  LocalToolSource-->>Agent: return transformed result
Loading

Possibly related PRs

Suggested labels: needs-human-input

Suggested reviewers: kwakayama

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: executing catalog integration tools with local credentials.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-495-byo-local-integration-credentials

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@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: d7eeae958b

ℹ️ 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/integrations/local-credential-auth.ts Outdated
Comment thread src/integrations/local-credential-auth.ts
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Exact-head follow-up 1ec401f addresses every current blocker:

  • RED/GREEN: Trusted Shops now reads ETRUSTED_CLIENT_ID and ETRUSTED_CLIENT_SECRET from catalog metadata.
  • RED/GREEN: catalog-declared OAuth token parameters now include the required encoded audience.
  • The failed Node hardening test was reproduced locally. Node native Headers.set depends on host String primordials, and node:test depends on Array.push while async tests are suspended. The tests now guard framework-owned traversal without poisoning the host constructor or test harness.

Verification: 173 integration steps GREEN, focused Deno 22 steps GREEN, focused Node 22 tests GREEN, typecheck GREEN, lint/docs GREEN, npm build GREEN, fmt/lint/diff checks GREEN. All current review threads are resolved.

@codex review
@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git show --no-ext-diff --format=fuller --stat 1ec401fd3
printf '\n--- changed files ---\n'
git diff-tree --no-commit-id --name-status -r 1ec401fd3

printf '\n--- relevant diff ---\n'
git show --no-ext-diff --format= --find-renames --find-copies 1ec401fd3 -- \
  | sed -n '1,1800p'

printf '\n--- candidate symbols ---\n'
rg -n -i -C 3 \
  'ETRUSTED_CLIENT_(ID|SECRET)|audience|tokenParameters|token parameters|Trusted Shops|Headers\.set|Array\.push|String\.prototype|primordials' \
  . \
  -g '!node_modules/**' -g '!dist/**' -g '!build/**' \
  | sed -n '1,1800p'

Length of output: 201004


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- commit metadata and changed files ---'
git show --no-ext-diff --format='commit %H%nparents %P%nsubject %s' --name-status 1ec401fd3

printf '%s\n' '--- complete follow-up patch ---'
git show --no-ext-diff --format= --find-renames --find-copies 1ec401fd3 \
  | sed -n '1,2200p'

printf '%s\n' '--- local credential auth outline ---'
ast-grep outline src/integrations/local-credential-auth.ts --items all --view expanded

printf '%s\n' '--- focused implementation and test evidence ---'
for file in \
  src/integrations/local-credential-auth.ts \
  src/integrations/local-credential-auth.test.ts \
  src/integrations/local-endpoint-executor.test.ts \
  src/integrations/_data.ts \
  src/integrations/_data.test.ts
do
  if [ -f "$file" ]; then
    printf '\n### %s\n' "$file"
    rg -n -C 8 \
      'trusted-shops|ETRUSTED_CLIENT|additionalAuthParams|token-request|client_credentials|audience|ambient collection|framework-owned traversal|Headers\.set' \
      "$file" || true
  fi
done

Length of output: 71393


@kojiwakayama: I reviewed follow-up commit 1ec401fd3.

The update resolves the catalog credential-name mismatch for Trusted Shops. It also copies validated catalog token parameters into client-credentials requests and encodes audience correctly.

The Node test changes avoid host-runtime and node:test internals while retaining checks for framework-owned traversal.

I found no new blocking issue in this follow-up.


🧠 Learnings used
Learnt from: kojiwakayama
Repo: veryfront/veryfront-code PR: 3327
File: src/tool/remote-mcp.ts:0-0
Timestamp: 2026-08-03T11:52:30.634Z
Learning: In `src/tool/internal/remote-mcp-transport.ts`, `REMOTE_MCP_HOST_TRANSPORT_AUTHORITY` must remain a plain module-local identity object. Do not create or freeze it through mutable globals because project code can modify primordials before the host-only module loads. In `src/tool/remote-mcp.ts`, `createCapabilityGuardedFetch` must use native `await` to validate the authority before it invokes caller-supplied `requestFetch`; this prevents a modified `Promise.prototype.then` from bypassing the validation. `src/tool/remote-mcp.test.ts` uses fresh-process regressions for hostile `Object.freeze` and `Promise.prototype.then` and verifies that `transportCalls` remains zero.
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 1ec401fd30

ℹ️ 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 docs/superpowers/specs/2026-08-18-local-integration-credentials-design.md Outdated
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex review

Record the explicit local source, credential-provider contract, supported authentication modes, egress controls, and fail-closed non-goals before implementation begins.

Constraint: Local integration credentials must never enter model-visible, persisted, logged, URL, or Veryfront API state
Rejected: Reuse integrations.allow as a grant surface | it is an established narrowing-only policy
Rejected: Fall back implicitly from the hosted remote source | transport and credential ownership would become ambiguous
Confidence: high
Scope-risk: broad
Directive: Keep the local source explicit and preserve the API-backed integration path unchanged
Tested: Markdown format, line-width audit, placeholder audit, and diff whitespace
Not-tested: Runtime behavior; implementation follows in RED-GREEN tasks
Translate the approved credential boundary into admission, authentication, endpoint execution, Salesforce, documentation, verification, and merge-gate tasks with explicit RED and GREEN evidence.

Constraint: Each credential-boundary behavior must be locked by a failing regression before implementation
Confidence: high
Scope-risk: broad
Directive: Do not merge until exact-head CI is green and all review threads are resolved
Tested: Markdown format, line-width audit, placeholder audit, and diff whitespace
Not-tested: Runtime behavior; plan execution starts next
Introduce an explicit catalog tool allowlist, snapshot it before project mutation, emit credential-free model schemas, and reject unsupported endpoint/auth contracts before any provider request exists.

Constraint: Environment credential presence must never enable a tool by itself
Rejected: Reuse catalog visibility as the local grant | explicit Salesforce service tools must work without widening public catalog/scaffolding visibility
Confidence: high
Scope-risk: moderate
Directive: Keep source construction as the grant and integrations.allow as narrowing-only
Tested: 5 focused Deno steps; deno check; deno fmt; deno lint; git diff --check
Not-tested: Credential resolution and provider execution follow in later TDD seams
Separate serializable catalog auth plans from short-lived resolved headers and token requests, validate readiness before tool exposure, and support API-key, Basic, client-credentials, and Salesforce service-account vocabularies.

Constraint: Credential providers receive only canonical environment-variable names and provider failures cannot carry secret causes
Rejected: Store raw credentials on the tool source | source objects and metadata are model/runtime-visible for longer than one request
Confidence: high
Scope-risk: moderate
Directive: Do not add raw credential values to plans, errors, contexts, caches, or logs
Tested: 13 focused Deno steps; deno check; deno fmt; deno lint; git diff --check
Not-tested: Provider HTTP transport and token-response handling follow in the endpoint execution seam
Catalog metadata now constructs exact-origin REST requests without exposing provider responses or transport failures. The executor rejects unknown or mistyped arguments before transport, applies defaults deterministically, rejects redirects, shares the guarded egress boundary, and bounds JSON response reads.

Constraint: Local integration credentials and provider response bodies must never enter errors, URLs outside the admitted origin, or unbounded memory.
Rejected: Reuse ordinary fetch directly | it would bypass redirect-hop and private-address enforcement.
Confidence: high
Scope-risk: moderate
Directive: Keep transport injection internal and preserve exact-origin authorization on every network hop.
Tested: 19 focused Deno steps; deno check; deno lint; deno fmt; anti-slop; diff and line-width checks.
Not-tested: Live third-party provider responses and token endpoints remain for the next slice.
The catalog-backed source now resolves project credentials at call time, mints short-lived OAuth client credentials through the guarded transport, validates Salesforce My Domain instance origins, and executes only the admitted tool endpoint. Local materialization keeps the existing source-policy narrowing and leaves the hosted API bridge unchanged.

Constraint: Salesforce service-account responses may supply an instance origin, but never an arbitrary provider URL or redirect target.
Rejected: Cache access tokens in the source | it would lengthen secret lifetime and complicate rotation and invalidation.
Confidence: high
Scope-risk: moderate
Directive: Keep the test transport seam internal; the public source accepts only exact tool grants and a name-only credential provider.
Tested: 110 focused Deno steps covering local execution, source policy, and the unchanged remote API bridge; deno check, lint, fmt, anti-slop, diff and line-width checks.
Not-tested: Live provider credentials and third-party availability.
The public guides now separate managed OAuth from the catalog-backed local credential path and lock the exact-grant and secret-isolation contracts with executable documentation tests.

Constraint: Local execution must not require or contact the Veryfront control plane.
Rejected: Document integrations.allow as credential selection | it is narrowing-only capability policy.
Confidence: high
Scope-risk: narrow
Directive: Keep managed per-user OAuth distinct from local service-account execution.
Tested: docs validation, guide contracts, executable examples, public copy checks, pinned Deno 2.7.7 API reference check
Not-tested: live provider credentials
Capture the collection, URL, abort, response, and string primitives used by local integration admission and execution. Adversarial tests now prove project code cannot replace those ambient primitives to widen grants, erase credentials, or skip bounded response cleanup.

Constraint: Project modules share the host realm before local sources list or execute tools.
Rejected: Trust ambient collection prototypes | project code can replace them after framework initialization.
Confidence: high
Scope-risk: moderate
Directive: Keep local integration admission and credential execution on captured primitives and own-property array writes.
Tested: 15 integration suites (215 steps), focused hardening suites (28 steps), production check, fmt, lint, anti-slop, test-typecheck baseline, docs validation, public docs, error docs, pinned API-reference generation/check.
Not-tested: Live provider credentials or production provider endpoints.
Review hardening captures credential-bearing catalog data before project code can mutate public exports, keeps credential identifiers out of model-visible definitions, reports only bounded provider diagnostics, and carries required Basic-auth headers.

Constraint: Local credential values and credential identifiers must never enter model metadata or provider diagnostics
Rejected: Re-read the mutable public connector catalog | project code can mutate it before source construction
Confidence: high
Scope-risk: narrow
Directive: Keep local admission on the private catalog snapshot and preserve exact-grant semantics
Tested: 15-suite integration and tool matrix (39 tests, 245 steps); docs validation; pinned API docs; npm build
Validate catalog credential metadata and tool arguments before credential resolution, treat successful no-content responses as null, register the local boundary errors centrally, and document the direct local execution boundary.

Constraint: No provider request or credential lookup may occur for an invalid tool invocation
Rejected: Rely only on endpoint execution validation | OAuth minting happens before endpoint execution
Confidence: high
Scope-risk: moderate
Directive: Keep argument admission before all credential work and preserve 204 as a successful empty result
Tested: 15-suite integration and tool matrix (38 tests, 243 steps); error registry (119 slugs); docs validation (1,385 links); deno check, lint, fmt, anti-slop, diff checks
Not-tested: Live provider credentials and third-party endpoints
The local-integration guide assertions now resolve their files from the test module URL, preserving deterministic reads when parallel tests temporarily change the shared process directory.

Constraint: Parallel test isolates share process cwd and the lint baseline cannot increase.
Rejected: Raise the cwd-relative-read baseline | would preserve a known race instead of removing it.
Confidence: high
Scope-risk: narrow
Directive: Resolve repository fixtures from import.meta.url in test callbacks.
Tested: lint:cwd-relative-test-reads; guide-content.test.ts; fmt; lint; check; diff-check
Not-tested: Full suite deferred to the existing pre-push hook and replacement CI.
@kwakayama
kwakayama force-pushed the feat/issue-495-byo-local-integration-credentials branch from 49ecfe5 to 9ab5b17 Compare August 18, 2026 05:30
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Rebased cleanly onto main d6b2f8b at exact head 9ab5b17. The catalog credential-name, token-parameter, Node portability, and design-contract fixes are preserved. All existing review threads remain resolved. Please review the full rebased head.

@codex review

@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: 9ab5b172b9

ℹ️ 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/integrations/local-credential-auth.ts Outdated
Comment thread src/integrations/local-credential-auth.ts Outdated
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Review follow-up at exact head 5fe6ca2:

  • Added RED-GREEN coverage for OAuth Basic form encoding, caller cancellation, pre-aborted credential resolution, and oversized response-body cleanup.
  • Switched the new implementation modules to the required #veryfront/integrations/... import-map paths.
  • Regenerated the public API reference after source anchors moved.

Verification: focused Deno 40/40 steps, all integration tests 176/176 steps, targeted type-checking, repository lint/static gates, generated docs, npm package build, focused Node 24/24 tests, focused Bun 2/2 files, formatting, and diff checks are green.

The full lint:ci run reached one unrelated floating-type failure in src/agent/runtime/provider-metadata-continuation.test.ts. It reproduces unchanged on the merged base with deno check --no-lock, while its locked check is green. Fresh exact-head CI is now authoritative for the PR.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 5fe6ca2cff

ℹ️ 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".

Resolve doc and doc-contract conflicts by combining both sides:
- self-hosting.md keeps the generic createLocalIntegrationToolSource
  example and restores main's Salesforce service-account section
- salesforce.md keeps the local catalog example and main's dedicated
  service-account cross-reference
- support-matrix keeps the split remote/local rows and notes the
  dedicated Salesforce service-account source
- guide-contracts pins the union of both sides' surviving snippets
- api-reference/integrations.md regenerated with pinned Deno 2.7.7
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Merged current main at exact head 42b2891dd to clear the merge conflict. Doc conflicts were resolved by combining both sides:

  • docs/guides/self-hosting.md keeps this branch's generic createLocalIntegrationToolSource example and restores main's Run Salesforce integration tools locally section (dedicated createSalesforceServiceAccountToolSource path).
  • docs/guides/integrations/salesforce.md keeps the local catalog example and main's dedicated service-account cross-reference; main's rewritten External Client App service-account steps are preserved.
  • docs/architecture/20-support-matrix.md keeps the split remote/local capability rows and notes the dedicated Salesforce service-account source.
  • tests/docs/guide-contracts.test.ts pins the union of both sides' surviving snippets (Admin approved users are pre-authorized dropped with the superseded steps).
  • docs/api-reference/veryfront/integrations.md regenerated with pinned Deno 2.7.7; docs:api-reference:check reports current.

Verification: tests/docs 57 passed (240 steps); src/integrations 14 files (192 steps); typecheck, lint:ci, deno fmt --check on changed files, and docs:check-links (1386 links) all pass; pre-push full suite green.

@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: c26c7254a5

ℹ️ 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/integrations/local-endpoint-executor.ts
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Both fixes are now on the branch at exact head a12cbe647da1, full pre-push suite green on push.

The deferred hosted-gate finding is filed as veryfront/veryfront-issue-inbox#595 (low), with the pod-level evidence that VERYFRONT_MODE is production and never hosted, so hosted refusal currently rests on PROXY_MODE=1 alone. It is correct today and deliberately not changed here — inverting a security gate from a deny-list to a positive marker is not something to fold into a feature PR at merge time.

For the record on the earlier CI noise: head c26c7254a finished with all 9 required checks SUCCESS and zero non-success checks, and the coverage shard 4/8 job that failed on attempt 1 passed on attempt 2 with no code change (job 95917793185). Confirmed flake, tracked as veryfront/veryfront-issue-inbox#594.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex please review the superseding exact head a12cbe647da1 — it adds one commit on top of c26c7254a, which my previous request covered.

New in this head (a12cbe647): safeIdentifier is exported as safeLocalIntegrationIdentifier from local-integration-errors.ts and applied to the model-supplied toolName in the not-granted configuration error at local-tool-source.ts. Previously that path interpolated the raw caller string into error metadata while the request/response paths were already sanitized.

Please check specifically:

  1. That exporting the helper does not widen anything unintended — it is the same function, previously private, now reachable from src/integrations/.
  2. That collapsing an unrecognized name to "unknown" does not degrade a legitimate diagnostic: well-formed catalog tool IDs (vercel__list_projects) pass the charset unchanged, and the pre-existing never executes a catalog tool outside its exact source grant test still asserts the "not granted" detail.
  3. Whether any other caller-supplied string still reaches error metadata unsanitized on the config path — snapshotOptions was named alongside this one in review and I did not change it; please confirm whether it genuinely needs the same treatment or whether its inputs are source-controlled rather than model-controlled.

Still the substantive change from the prior head, please keep it in scope: the pathSegment guard closing same-origin path collapse via ../. path arguments.

Exact-head GREEN: src/integrations/ + src/errors/ 45 passed (714 steps); deno fmt/lint/check clean on all touched files; full pre-push suite passed on push. Both fixes are RED-first and mutation-checked. All review threads resolved (9 total, 0 unresolved). Two review findings deliberately deferred to their own issues rather than folded in here: veryfront/veryfront-issue-inbox#595 (hosted gate shape) and #594 (unrelated CI flake).

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Answering my own question 3 above, since I traced it rather than leaving it open:

snapshotOptions does not need the same treatment, and neither does any other config-error site. toolName in executeTool was the only model-controlled string reaching one. The distinction is provenance, not shape:

site interpolated value origin
executeTool not-granted (:539) toolName model — now sanitized in a12cbe647
readOwnDataProperty (:167) String(key) module-internal: the only call sites pass the literals "tools", "credentialProvider", or a numeric array index
invalid tool metadata (:249) canonicalToolId catalog-derived (tool.id ?? tool.name, optionally prefixed with connector.name)
missing API-key / Basic / token URL (:319, :326, :333) connector.name catalog-derived

The catalog and the options object are supplied by the developer wiring the source up, not by the model, and those errors are thrown at configuration time before any tool call. Sanitizing them would actively hurt: collapsing a genuinely malformed catalog ID to "unknown" removes the one piece of information that makes the error actionable.

So the rule this settles on is: sanitize where the value crosses the model boundary, keep it verbatim where it is developer configuration. Only executeTool sits on the model side.

Also worth noting the sanitized name is not the only signal in that message — the "is not granted by this source" wording plus the local-integration-config-invalid slug still identify the failure precisely, and the pre-existing never executes a catalog tool outside its exact source grant test continues to assert the "not granted" detail with a well-formed ID passing through unchanged.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: a12cbe647d

ℹ️ 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".

Regenerated with `deno task docs` on the CI-pinned Deno 2.7.7. The sanitization
commit shifted three declarations in `local-tool-source.ts`; only the line pins
move and no documented symbol changes.

Refs veryfront/veryfront-issue-inbox#495
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

ci (lint) fixed — generated API reference was stale

The failure on a12cbe647 was not a code defect:

docs/api-reference is stale:
  outdated: veryfront/integrations.md
Run `deno task docs` and commit the result to update the generated API reference.

The sanitization commit widened the import statement and split the configurationError call across lines, which shifted three declarations in local-tool-source.ts. The generated reference pins source line numbers, so it went stale.

Regenerated with deno task docs on the CI-pinned Deno 2.7.7 (local default is 2.7.12; using the wrong one produces spurious staleness elsewhere). Result is exactly the one file CI named, and the diff is three line pins and nothing else:

symbol pin
createLocalIntegrationToolSource L581 -> L588
LocalIntegrationCredentialProvider L55 -> L58
LocalIntegrationToolSourceOptions L60 -> L63

No documented symbol was added, changed, or removed.

That also answers question 1 from my review request: exporting safeLocalIntegrationIdentifier did not widen the public API surface. It is absent from the regenerated reference because local-integration-errors.ts is not re-exported through src/integrations/index.ts; the module stays internal and the helper's only consumer in src/ is the call site it was added for. Verified by grep, not inferred from the docs alone.

Note the other 8 required checks were already green on a12cbe647ci (lint) was the only failure, and coverage shard 4/8 passed this time.

…redentials

The dot-segment guard ran only in `buildRequest`, which `executeTool` reaches
after `mintLocalCredentialAuth`. For a client-credentials tool such as
`paypal__get_invoice`, an invalid `{ invoiceId: ".." }` therefore completed an
OAuth token round trip -- sending the client credentials to the token endpoint --
before anything rejected it. The pre-auth argument snapshot only coerced path
values with `scalarString`, so it let the value through.

`snapshotLocalIntegrationEndpointArguments` now validates path arguments with
the same `pathSegment` helper the builder uses, so the rejection happens before
any credential is resolved or minted and before any transport call. Header
arguments keep their `scalarString` coercion; only path values carry
path-segment semantics.

The build-time guard stays. The two are deliberately redundant: the executor is
exported and callable directly, so it cannot rely on a caller having snapshotted
first.

Reported by Codex review on this PR.

Refs veryfront/veryfront-issue-inbox#495
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Pre-auth ordering fix pushed: 60303e205 — exact head for review

Codex's P2 on the previous head was correct and is fixed. Recording it here as well as on the thread, because it changes a security property of the feature rather than just a line of code.

What was wrong

The dot-segment guard I added in c26c7254a lived only in buildRequest. executeTool reaches that after mintLocalCredentialAuth:

executeTool -> snapshotLocalIntegrationEndpointArguments   (scalarString only -> ".." passes)
            -> mintLocalCredentialAuth                     (client_credentials token round trip)
            -> executeLocalIntegrationEndpoint -> buildRequest -> pathSegment  (rejects here)

So paypal__get_invoice with { invoiceId: ".." } sent the client credentials to the OAuth token endpoint before being rejected. Not a credential leak — the token endpoint is the admitted one — but it lets model-supplied garbage force credential minting and outbound OAuth traffic, and it contradicted this PR's own stated pre-auth validation contract.

Evidence it was real, not theoretical

The RED test failed with slug local-integration-response-invalid, not local-integration-request-invalid. That is the tell: execution had already passed credential minting and a transport call, and only died parsing a response. A guard that merely "rejects eventually" would have produced request-invalid.

Fix

snapshotLocalIntegrationEndpointArguments validates path arguments with the same pathSegment helper the builder uses. Header arguments keep scalarString — only path values carry path-segment semantics.

The build-time guard is kept on purpose: executeLocalIntegrationEndpoint is exported and directly callable, so it cannot assume its caller snapshotted first. pathSegment is idempotent, so the redundancy is free.

Verification

  • New test asserts credentialProviderCalls === 0 and transportCalls === 0 for "..", ".", "" through the real executeTool path.
  • Mutation-checked against the exact concern: reverting only the snapshot line to scalarString, leaving the build-time guard intact, turns the new test red. The test therefore binds to the pre-auth placement, not merely to rejection happening somewhere.
  • src/integrations/ + src/errors/: 45 passed (715 steps), 0 failed. deno fmt/lint/check clean. deno task docs on pinned Deno 2.7.7 produced no doc changes. Full pre-push suite passed on push.

@codex please review the exact current head 60303e205760. The substantive change is the validation moving into the pre-auth snapshot; please check whether any other argument class reaching executeTool can still trigger credential minting before validation, and whether the header branch should carry its own restriction.

All review threads resolved (10 total, 0 unresolved).

@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: 60303e2057

ℹ️ 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/integrations/local-endpoint-executor.ts Outdated
The same ordering defect the path guard just fixed also applied to header
arguments. The pre-auth snapshot only coerced them with `scalarString`, while
the rejection for a value the `Headers` setter refuses happened in
`buildRequest` -- after `mintLocalCredentialAuth`.

`mongodb-atlas__list_projects` is client-credentials and takes an `accept`
header argument, so a value containing CRLF completed an OAuth token round trip
before anything rejected it. 87 catalog tools expose a header argument on an
OAuth connector, 4 of them on a client-credentials one.

The snapshot now runs the argument through the same `setHeader` helper against
a throwaway `Headers`, so the header name and value are validated by exactly
the code that will later set them, with no second copy of the rules.

The invariant this settles on: every argument class is fully validated before
any credential is resolved or minted.

Refs veryfront/veryfront-issue-inbox#495
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

The same ordering defect applied to header arguments — fixed

I asked in my last comment whether any other argument class could still trigger credential minting before validation. I traced it rather than waiting for an answer, and yes, header arguments had exactly the same defect.

Reachability, measured against the catalog

tools with a header argument on an OAuth connector: 87
of which client-credentials (i.e. a token round trip):  4

mongodb-atlas__list_projects is client-credentials and takes an accept header argument. Calling it with { accept: "application/json\r\nx: y" } reproduced the identical signature to the path case: slug local-integration-response-invalid, not request-invalid — meaning credential minting and a transport call had already happened. setHeader does reject the value correctly, just too late, because it only runs in buildRequest.

Fix

The pre-auth snapshot now runs header arguments through the same setHeader helper against a throwaway Headers:

if (field.in === "path") pathSegment(resolved.value);
else if (field.in === "header") {
  setHeader(new HeadersConstructor(), field.headerName ?? name, scalarString(resolved.value));
}

Validating through the same code that will later set the header means there is no second copy of the rules to drift — the header name is checked too, not just the value.

Verification

  • RED first, then GREEN: src/integrations/ + src/errors/ 45 passed (716 steps), 0 failed.
  • Mutation-checked: reverting only the header branch to scalarString turns the new test red.
  • New test asserts credentialProviderCalls === 0 and transportCalls === 0.
  • deno fmt/lint/check clean; deno task docs on pinned Deno 2.7.7 produced no doc changes.

The invariant this settles on

Every argument class is fully validated before any credential is resolved or minted. Query and body arguments were already covered by fieldValue in the same pre-auth snapshot; path and header were the two that deferred part of their validation to build time. Both now validate up front, and both keep their build-time check because executeLocalIntegrationEndpoint is exported and directly callable.

Worth noting for the record: this is the second defect of this exact shape, and the first was only caught because Codex checked the ordering rather than the guard itself. The guard being correct said nothing about where it ran.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex review the exact current head 76ee7311738f. This supersedes my request on 60303e205760; it adds the header-argument half of the same pre-auth ordering fix.

Both argument classes that previously deferred validation to buildRequest now validate in snapshotLocalIntegrationEndpointArguments, which runs before mintLocalCredentialAuth:

if (field.in === "path") pathSegment(resolved.value);
else if (field.in === "header") {
  setHeader(new HeadersConstructor(), field.headerName ?? name, scalarString(resolved.value));
}

Please check specifically:

  1. Is the invariant actually complete now? The claim is that every argument class is fully validated before any credential is resolved or minted. Query and body arguments go through fieldValue in the same snapshot loop; path and header are handled above. Is there any remaining input — argument, endpoint field, or otherwise — whose rejection still happens after mintLocalCredentialAuth? serializeJson for the body runs at build time and is the case I am least sure about.
  2. Header validation via a throwaway Headers. I chose to validate through the same setHeader helper rather than re-implementing the rules, so name and value are checked by exactly the code that later sets them. Is constructing a discarded Headers per header argument acceptable here, and does Headers.set validation actually match between the scratch instance and the real one (no inherited or guard-mode differences)?
  3. Double validation. pathSegment and setHeader now run twice for the normal success path — once in the snapshot, once in buildRequest. Both are idempotent and pure. The build-time copies are kept deliberately because executeLocalIntegrationEndpoint is exported and callable without the snapshot. Confirm that is the right trade rather than routing all callers through the snapshot.

Reachability that motivated it, measured against the catalog: 87 tools take a header argument on an OAuth connector, 4 on a client-credentials one. mongodb-atlas__list_projects with { accept: "application/json\r\nx: y" } reproduced slug local-integration-response-invalid before the fix, proving the token round trip had already happened.

Exact-head GREEN: src/integrations/ + src/errors/ 45 passed (716 steps); both fixes RED-first and mutation-checked; deno fmt/lint/check clean; deno task docs on pinned Deno 2.7.7 produced no changes; full pre-push suite passed on push. All review threads resolved.

@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: 76ee731173

ℹ️ 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/integrations/local-endpoint-executor.ts Outdated
…entials

Validating each body field on its own proves nothing about the size of the body
they add up to, because an omitted field still contributes its catalog default.
Supplied arguments could therefore pass the pre-auth snapshot while the body
built from them exceeded the 4 MiB JSON bound, leaving `serializeJson` in
`buildRequest` to reject it -- after `mintLocalCredentialAuth`.

`salesforce__create_case` is the reachable case: four supplied strings just under
the 1 MiB per-string cap serialize to under the limit, and the `Status` and
`Origin` defaults carry the assembled body over it. The credential provider and
the token endpoint were both invoked before the rejection.

The body assembly is extracted as `assembleBody` and called from both the
snapshot and `buildRequest`, so the bound is checked against the body that will
actually be sent rather than against a reconstruction that could drift from it.

Reported by Codex review on this PR.

Refs veryfront/veryfront-issue-inbox#495
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex review the exact current head 85cad0e25c98. This is the head I intend to merge, and it is one commit past your last review (76ee73117).

New in this head: your assembled-body finding is fixed. Body assembly is extracted as assembleBody(endpoint, args) and called from both the pre-auth snapshot and buildRequest, so the 4 MiB bound is checked against the body that will actually be sent, defaults included, before mintLocalCredentialAuth runs.

Please focus on:

  1. Whether the invariant is now actually complete. The claim: every argument class — path, header, query, body — is fully validated before any credential is resolved or minted. Path and header validate in the snapshot; query and body go through fieldValue, and the body is now assembled and bound-checked there too. Is there any remaining input whose rejection still happens after mintLocalCredentialAuth?

  2. The shared-assembly choice. assembleBody runs twice per successful call — once in the snapshot (result discarded), once in buildRequest. I chose sharing over a separate size check specifically so the checked body cannot drift from the sent one. Please confirm that is sound, and flag if double-serializing a body up to 4 MiB is an unacceptable cost on the hot path versus, say, threading the assembled string through.

  3. Behavioural equivalence of the extraction. assembleBody is a lift-and-shift of the previous inline logic, including the passthrough branch and its bodyFields.length !== 1 guard. That guard now also fires during the snapshot rather than only at build time. Please check the extraction preserved semantics exactly, particularly for bodyMode: "passthrough" and for endpoints with no body fields.

Verification at this head: src/integrations/ + src/errors/ 45 passed (717 steps); deno task test:unit exit 0; deno task test:integration 303 passed (2834 steps), 0 failed; deno fmt/lint/check clean; deno task docs on pinned Deno 2.7.7 produced no changes. All 12 review threads resolved, 0 unresolved. All 9 required CI checks green.

Note: one pre-push run on this commit failed on the unrelated module-persistence leak-sanitizer flake (veryfront/veryfront-issue-inbox#594). I re-ran both suites locally at this exact commit before retrying — both clean — so the flake is not masking anything here.

@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: 85cad0e25c

ℹ️ 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/integrations/local-endpoint-executor.ts Outdated
Sharing `assembleBody` between the pre-auth snapshot and `buildRequest` made
both calls run the same code, but not in the same environment. Caller-supplied
credential code runs between them, and it can install an
`Object.prototype.toJSON`. `JSON.stringify` honours an inherited `toJSON` for
the ordinary nested objects a snapshot contains -- a null-prototype outer record
does not help -- so the second assembly could produce a completely different
body from the one that was bounded.

Measured, not theorised: with a provider that poisons the prototype while
resolving the credential, a `brevo__create_contact` call sent the literal body
`"POISONED"` in place of the contact payload. The bound check is bypassed
entirely, and the value sent to the provider under the project's credential is
whatever that code chooses.

`snapshotLocalIntegrationEndpointArguments` now returns the serialized body
alongside the arguments, and `executeTool` threads it into the executor, which
sends it verbatim. Direct executor callers still assemble locally: they have no
credential step for the environment to change across.

Reported by Codex review on this PR.

Refs veryfront/veryfront-issue-inbox#495
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex review the exact current head da9654cf6e03, one commit past your last review (85cad0e25).

New in this head: your finding is fixed by threading rather than by hardening serialization. now returns { args, body }; executeTool passes the pre-auth body into ExecuteLocalIntegrationEndpointOptions, and buildRequest sends it verbatim instead of re-assembling.

Please focus on:

  1. Does threading actually close it, or just move it? The bytes are now serialized once, before credential resolution, and passed through as a string. I believe nothing between the snapshot and the transport can alter a primitive string, but please check the path for anywhere the body is re-derived, re-parsed, or re-serialized — including the Content-Type branch and the passthrough case.

  2. Direct executor callers. executeLocalIntegrationEndpoint still snapshots locally when options.body is absent, so a caller invoking it directly gets one assembly and no credential step in between. Is that reasoning right, or can a direct caller still straddle a credential boundary I have not considered?

  3. The case. body is string | undefined; an endpoint with no body fields yields undefined, and options.body ?? snapshot.body then falls through to the local snapshot's undefined. Confirm that a genuinely body-less endpoint cannot be made to re-assemble, and that ?? is not masking a meaningful undefined from a caller that intended "no body".

I deliberately did not take your alternative ("make serialization immune to prototype changes"): immunity would require rebuilding every nested object with a null prototype, which is more code and regresses silently, whereas threading makes sending unchecked bytes structurally impossible. Push back if you disagree.

Exact-head GREEN: src/integrations/ + src/errors/ 45 passed (718 steps), 0 failed; deno fmt/lint/check clean; deno task docs on pinned Deno 2.7.7 changed one source line pin only. RED-first and mutation-checked. All 13 review threads resolved, 0 unresolved.

Housekeeping note: the docs regeneration also dirtied deno.lock and templates/manifest.generated.ts; both are unrelated to this change and were reverted rather than committed.

@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Correction to my previous comment — three phrases were eaten by shell backtick substitution before posting. Reposting intact; please review against this one.

@codex review the exact current head da9654cf6e03, one commit past your last review (85cad0e25).

New in this head: your Object.prototype.toJSON finding is fixed by threading rather than by hardening serialization. snapshotLocalIntegrationEndpointArguments now returns { args, body }; executeTool passes the pre-auth body into ExecuteLocalIntegrationEndpointOptions, and buildRequest sends it verbatim instead of re-assembling.

Please focus on:

  1. Does threading actually close it, or just move it? The bytes are now serialized once, before credential resolution, and passed through as a string. I believe nothing between the snapshot and the transport can alter a primitive string, but please check the path for anywhere the body is re-derived, re-parsed, or re-serialized — including the Content-Type branch and the passthrough case.

  2. Direct executor callers. executeLocalIntegrationEndpoint still snapshots locally when options.body is absent, so a caller invoking it directly gets one assembly and no credential step in between. Is that reasoning right, or can a direct caller still straddle a credential boundary I have not considered?

  3. The undefined case. body is string | undefined; an endpoint with no body fields yields undefined, and options.body ?? snapshot.body then falls through to the local snapshot's undefined. Confirm that a genuinely body-less endpoint cannot be made to re-assemble, and that ?? is not masking a meaningful undefined from a caller that intended "no body".

I deliberately did not take your alternative ("make serialization immune to prototype changes"): immunity would require rebuilding every nested object with a null prototype, which is more code and regresses silently, whereas threading makes sending unchecked bytes structurally impossible. Push back if you disagree.

Exact-head GREEN: src/integrations/ + src/errors/ 45 passed (718 steps), 0 failed; deno fmt/lint/check clean; deno task docs on pinned Deno 2.7.7 changed one source line pin only. RED-first and mutation-checked. All 13 review threads resolved, 0 unresolved.

Housekeeping note: the docs regeneration also dirtied deno.lock and templates/manifest.generated.ts; both are unrelated to this change and were reverted rather than committed.

@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: da9654cf6e

ℹ️ 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/integrations/local-endpoint-executor.ts Outdated
…mbled

Threading the pre-auth body stopped the executor from *sending* a re-serialized
body, but it still *performed* the second assembly and discarded the result.
That is invisible when a poisoned `toJSON` returns a value and fatal when it
throws: any endpoint with a nested or passthrough body still failed after the
credential had been resolved.

Argument validation is split from body serialization.
`snapshotAndValidateArguments` does the argument work alone, and the public
snapshot is that plus one assembly. The executor validates arguments, then
assembles only when no pre-auth body was threaded in, so a caller that already
serialized never serializes twice.

The previous test could not see this, because its `toJSON` returned a string
rather than throwing, so the redundant assembly succeeded and its result was
dropped. The new test throws instead.

Reported by Codex review on this PR.

Refs veryfront/veryfront-issue-inbox#495
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

@codex review the exact current head 54c379ffba5a, one commit past your last review (da9654cf6).

New in this head: your "skip reassembling a body that was already snapshotted" finding is fixed by splitting, not by gating. snapshotAndValidateArguments now does argument validation alone; the public snapshotLocalIntegrationEndpointArguments is that plus exactly one assembleBody. The executor is now:

const args = snapshotAndValidateArguments(options.endpoint, options.args);
const body = options.body ?? assembleBody(options.endpoint, args);

Please focus on:

  1. Is there now exactly one serialization per request, on every path? The intent is that no code path lets a caller-supplied body coexist with a second assembleBody call. Please check the direct-executor path, the executeTool path, the passthrough branch, and the body-less-endpoint case, and say if any of them can still serialize twice or zero times.

  2. Did splitting drop any validation? snapshotAndValidateArguments should be behaviourally identical to the old snapshot minus body assembly — same snapshotArguments, assertKnownArguments, and the same path/header parameter loop. Please confirm nothing that used to run for body fields silently stopped running. In particular the old code called fieldValue for every body field during validation; that now happens only inside assembleBody. For a threaded-body caller, assembleBody already ran pre-auth, so required-field and type errors still surface before minting — but I would like that checked rather than assumed.

  3. Whether the two poison tests together are actually sufficient. Your critique of the previous test was correct: a returning toJSON cannot detect a discarded second assembly. I kept both — the returning one pins which bytes are sent, the throwing one pins that no second serialization occurs. Is there a third poison shape (getter on Object.prototype, Symbol.toPrimitive, a toJSON that mutates rather than throws or returns) that would slip past both?

Exact-head GREEN: src/integrations/ + src/errors/ 45 passed (719 steps), 0 failed; deno fmt/lint/check clean on all 27 files in the directory; deno task docs produced no changes. RED-first and mutation-checked — restoring the unconditional snapshot call turns the new test red while the older one stays green, which is exactly the blind spot you identified. All 14 review threads resolved, 0 unresolved.

Context worth stating plainly: this is the seventh finding on this PR, and the last four form a chain in which each fix left a smaller version of the same hole — guard ran too late, then checked the wrong object, then the environment moved between check and use, then the redundant work still executed. If you see a fifth link in that chain, I would rather hear it now than merge.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 54c379ffba

ℹ️ 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".

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 19, 2026
Merged via the queue into main with commit c4e93cd Aug 19, 2026
34 checks passed
@kojiwakayama
kojiwakayama deleted the feat/issue-495-byo-local-integration-credentials branch August 19, 2026 03:01
@kwakayama kwakayama mentioned this pull request Aug 19, 2026
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