Skip to content

feat(sandbox-tenki): add Tenki sandbox provider - #1387

Open
monoppa wants to merge 1 commit into
VoltAgent:mainfrom
monoppa:feat/sandbox-tenki
Open

feat(sandbox-tenki): add Tenki sandbox provider#1387
monoppa wants to merge 1 commit into
VoltAgent:mainfrom
monoppa:feat/sandbox-tenki

Conversation

@monoppa

@monoppa monoppa commented Jul 30, 2026

Copy link
Copy Markdown

PR Checklist

Bugs / Features

What is the current behavior?

VoltAgent ships first-party workspace sandbox providers for Blaxel, Daytona, and E2B. There is none for Tenki, so anyone who wants agents to run shell commands in Tenki microVMs has to implement WorkspaceSandbox themselves.

What is the new behavior?

Adds @voltagent/sandbox-tenki, a WorkspaceSandbox implementation backed by @tenkicloud/sandbox, mirroring the existing E2B / Blaxel / Daytona providers. Each TenkiSandbox lazily provisions one disposable Tenki Linux microVM and reuses it across every execute_command:

  • streaming stdout/stderr, with maxOutputBytes truncation on UTF-8 codepoint boundaries
  • per-call timeoutMs and AbortSignal, enforced by killing the remote process
  • native cwd / env / stdin forwarding
  • start() / stop() / destroy() with serialized lifecycle transitions; a paused session is resumed (and timeout/abort re-checked) before a command runs
  • getSandbox() for direct SDK access (filesystem, port exposure, SSH), plus getInfo() / getInstructions() for the workspace toolkit
  • createTenkiToolkit(sandbox) — optional expose_preview_url and authorize_ssh_key tools on the same session, isolated in tools.ts so they can be dropped without touching the adapter

No changes to @voltagent/core. Also included: a package README, a ### Tenki section plus provider-table row in website/docs/workspaces/sandbox.md, and a minor changeset.

Notes for reviewers

  • Disclosure: I work with Tenki team.

  • CI: no sandbox provider is currently in the test-packages matrix, so this suite (like sandbox-blaxel's) will not run upstream. I deliberately left the workflow files untouched — adding sandbox-tenki to release.yml would make it a gate on publish, which isn't my call. Glad to add all four providers to the matrix in a follow-up if you'd like them there.

  • E2E Tests runs rather than skips on this PR because its path filter matches pnpm-lock.yaml. Nothing here touches an e2e input.

  • Validation: 133 tests, 100% line/branch/function/statement coverage (SDK fully mocked — no network, no credentials), plus pnpm install --frozen-lockfile, pnpm lint:ci, pnpm sp lint, pnpm build:all, pnpm publint:all, typecheck, and commitlint. No live microVM run — there is no TENKI_API_KEY in this environment.


Summary by cubic

Add @voltagent/sandbox-tenki, a new sandbox provider that runs agent commands in a disposable Tenki Linux microVM via @tenkicloud/sandbox. This brings Tenki support alongside the E2B, Blaxel, and Daytona adapters.

  • New Features
    • Implements WorkspaceSandbox; lazily provisions one Tenki session and reuses it across execute_command.
    • Streaming stdout/stderr with UTF‑8–safe truncation (maxOutputBytes), per-call timeoutMs and AbortSignal, and native cwd/env/stdin forwarding.
    • Lifecycle control: start(), stop(), destroy() with serialized transitions; paused sessions resume before execution.
    • Escape hatch: getSandbox() exposes the Tenki session; also supports getInfo()/getInstructions().
    • Optional toolkit: createTenkiToolkit(sandbox) adds expose_preview_url and authorize_ssh_key.
    • Docs: adds package README and a Tenki section in sandbox docs; no changes to @voltagent/core.

Written for commit 94889cb. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added Tenki as a remote workspace sandbox provider for running agent commands in disposable Linux microVMs.
    • Supports streaming output, timeouts, cancellation, environment and directory configuration, input forwarding, and output limits.
    • Added lifecycle controls, preview URL exposure, SSH key authorization, and direct SSH access.
    • Added the @voltagent/sandbox-tenki package with setup guidance and configuration examples.
  • Documentation

    • Added comprehensive Tenki provider documentation, usage examples, credential guidance, and cleanup recommendations.

Add @voltagent/sandbox-tenki, a WorkspaceSandbox provider that runs each
execute_command inside a disposable Tenki Linux microVM via
@tenkicloud/sandbox, mirroring the existing E2B / Blaxel / Daytona providers.

TenkiSandbox is built on the SDK's session run() API, which returns a process
handle and is the only primitive that can enforce a per-command timeout and
AbortSignal. It forwards cwd/env/stdin natively, keeps stdout and stderr
separate, truncates each stream at maxOutputBytes on a UTF-8 character
boundary, and serializes start/stop/destroy so concurrent lifecycle calls
observe one transition at a time.

createTenkiToolkit adds optional expose_preview_url and authorize_ssh_key
tools, kept in tools.ts so consumers can omit them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 94889cb

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

This PR includes changesets to release 1 package
Name Type
@voltagent/sandbox-tenki 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

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds @voltagent/sandbox-tenki, a Tenki-backed WorkspaceSandbox provider with microVM command execution, streaming, timeouts, lifecycle controls, SSH authorization, preview URL tools, tests, package configuration, and documentation.

Changes

Tenki provider package

Layer / File(s) Summary
Provider package and public surface
.changeset/sandbox-tenki.md, packages/sandbox-tenki/*, packages/sandbox-tenki/src/index.ts, packages/sandbox-tenki/src/sandbox.ts, website/docs/workspaces/sandbox.md
Defines package entrypoints, exports TenkiSandbox and its types, configures builds and tests, and adds Tenki to the provider documentation.
Command execution and output handling
packages/sandbox-tenki/src/utils.ts, packages/sandbox-tenki/src/sandbox.ts, packages/sandbox-tenki/src/*spec.ts
Implements streamed command execution with environment, working directory, stdin, timeout, abort, diagnostics, UTF-8-safe truncation, and result mapping.
Session lifecycle and SSH authorization
packages/sandbox-tenki/src/sandbox.ts, packages/sandbox-tenki/src/sandbox.spec.ts, packages/sandbox-tenki/README.md
Adds lazy provisioning, pause/resume, destruction, session reuse, status reporting, and serialized additive SSH key authorization.
Toolkit and Tenki usage documentation
packages/sandbox-tenki/src/tools.ts, packages/sandbox-tenki/src/tools.spec.ts, packages/sandbox-tenki/README.md, website/docs/workspaces/sandbox.md
Adds validated preview URL and SSH authorization tools and documents Tenki session access, networking, SSH, cleanup, and routing examples.

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

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant TenkiSandbox
  participant TenkiSession
  Agent->>TenkiSandbox: Request sandbox command
  TenkiSandbox->>TenkiSession: Run command with execution options
  TenkiSession-->>TenkiSandbox: Stream stdout and stderr
  TenkiSandbox-->>Agent: Return execution result and streamed output
Loading

Possibly related PRs

  • VoltAgent/voltagent#1068: Adds a related getSandbox() accessor pattern for exposing an underlying sandbox provider session.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding the Tenki sandbox provider.
Description check ✅ Passed The description mostly matches the template and includes checklist items, behavior sections, tests, docs, changesets, and reviewer notes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/sandbox-tenki/src/tools.ts (1)

15-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a narrow typed sandbox dependency.

The factory only needs getSandbox and authorizeSshKey, but requiring TenkiSandbox forces the test to bypass type checking with as unknown as TenkiSandbox.

  • packages/sandbox-tenki/src/tools.ts#L15-L15: accept an exported Pick<TenkiSandbox, "getSandbox" | "authorizeSshKey"> (or equivalent interface).
  • packages/sandbox-tenki/src/tools.spec.ts#L10-L13: type the mock against that narrow contract without a double assertion.

As per coding guidelines, **/*.ts: Maintain type safety in TypeScript-first codebase.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-tenki/src/tools.ts` at line 15, Update createTenkiToolkit in
packages/sandbox-tenki/src/tools.ts:15-15 to accept an exported narrow contract
containing only getSandbox and authorizeSshKey, such as Pick<TenkiSandbox,
"getSandbox" | "authorizeSshKey">. In
packages/sandbox-tenki/src/tools.spec.ts:10-13, type the mock against that
narrow contract and remove the as unknown as TenkiSandbox double assertion.

Source: Coding guidelines

packages/sandbox-tenki/src/sandbox.spec.ts (2)

213-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

vi.clearAllMocks() clears calls but keeps implementations, so mocks.createAndWait implementations set with mockResolvedValue/mockReturnValue (e.g. Line 647's never-resolving promise) leak into subsequent tests. Every test that relies on createAndWait currently re-sets it, but this makes the suite order-dependent. Prefer resetting implementations.

♻️ Proposed fix
 beforeEach(() => {
-  vi.clearAllMocks();
+  vi.resetAllMocks();
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-tenki/src/sandbox.spec.ts` around lines 213 - 215, Update
the sandbox.spec.ts beforeEach setup to reset mock implementations, not only
invocation history, so mocks.createAndWait does not retain mockResolvedValue or
mockReturnValue behavior across tests; use the appropriate Vitest reset
operation while preserving the existing per-test setup.

155-160: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unused mock scaffolding. stdin/_writeSpy are never exercised — the adapter passes its own stdin ReadableStream in runOptions (verified by the stdin test at Line 359) and never writes to the handle's stdin. Consider dropping them.

Also applies to: 176-176

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-tenki/src/sandbox.spec.ts` around lines 155 - 160, Remove
the unused writeSpy mock and stdin WritableStream declaration from the test
setup around the adapter invocation, including the corresponding _writeSpy
scaffolding. Keep the runOptions stdin ReadableStream used by the stdin test
unchanged, since the handle’s stdin is never exercised.
packages/sandbox-tenki/README.md (1)

107-109: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a language to the fenced block to satisfy markdownlint MD040.

📝 Proposed fix
-```
+```text
 tenki: exec failed: ENOENT (errno 2), reason=exec_failed
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @packages/sandbox-tenki/README.md around lines 107 - 109, Update the fenced
code block in the README to specify the text language identifier, preserving its
existing contents so it satisfies markdownlint MD040.


</details>

<!-- cr-comment:v1:8bf42207551c9adc25f15ebd -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @packages/sandbox-tenki/src/tools.ts:

  • Around line 53-56: Update the publicKey schema in the tool definition to
    reject whitespace-only values and any values containing newline characters
    before updateSshAuthorizedKeys is invoked. Preserve valid single-line
    authorized_keys values, and add matching schema cases covering blank,
    whitespace-only, and multiline inputs.

Nitpick comments:
In @packages/sandbox-tenki/README.md:

  • Around line 107-109: Update the fenced code block in the README to specify the
    text language identifier, preserving its existing contents so it satisfies
    markdownlint MD040.

In @packages/sandbox-tenki/src/sandbox.spec.ts:

  • Around line 213-215: Update the sandbox.spec.ts beforeEach setup to reset mock
    implementations, not only invocation history, so mocks.createAndWait does not
    retain mockResolvedValue or mockReturnValue behavior across tests; use the
    appropriate Vitest reset operation while preserving the existing per-test setup.
  • Around line 155-160: Remove the unused writeSpy mock and stdin WritableStream
    declaration from the test setup around the adapter invocation, including the
    corresponding _writeSpy scaffolding. Keep the runOptions stdin ReadableStream
    used by the stdin test unchanged, since the handle’s stdin is never exercised.

In @packages/sandbox-tenki/src/tools.ts:

  • Line 15: Update createTenkiToolkit in
    packages/sandbox-tenki/src/tools.ts:15-15 to accept an exported narrow contract
    containing only getSandbox and authorizeSshKey, such as Pick<TenkiSandbox,
    "getSandbox" | "authorizeSshKey">. In
    packages/sandbox-tenki/src/tools.spec.ts:10-13, type the mock against that
    narrow contract and remove the as unknown as TenkiSandbox double assertion.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `cbead9fa-c9af-4414-ba84-70e0f150afab`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3377f6d1fd05d86617f54628d785eef7c9c0a607 and 94889cb8d4fb24e09a5157de6aac6e54aa38e4b6.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (15)</summary>

* `.changeset/sandbox-tenki.md`
* `packages/sandbox-tenki/README.md`
* `packages/sandbox-tenki/package.json`
* `packages/sandbox-tenki/src/index.spec.ts`
* `packages/sandbox-tenki/src/index.ts`
* `packages/sandbox-tenki/src/sandbox.spec.ts`
* `packages/sandbox-tenki/src/sandbox.ts`
* `packages/sandbox-tenki/src/tools.spec.ts`
* `packages/sandbox-tenki/src/tools.ts`
* `packages/sandbox-tenki/src/utils.spec.ts`
* `packages/sandbox-tenki/src/utils.ts`
* `packages/sandbox-tenki/tsconfig.json`
* `packages/sandbox-tenki/tsup.config.ts`
* `packages/sandbox-tenki/vitest.config.ts`
* `website/docs/workspaces/sandbox.md`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +53 to +56
publicKey: z
.string()
.describe("SSH public key in authorized_keys format (e.g. 'ssh-ed25519 AAAA... user')"),
}),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject blank and multiline SSH-key values.

The schema accepts empty/whitespace-only strings and newline-delimited values, then forwards them to updateSshAuthorizedKeys. Reject obvious non-entries before provisioning or mutating the session, and add matching schema cases.

Proposed fix
       publicKey: z
         .string()
+        .refine(
+          (value) => value.trim().length > 0 && !/[\r\n]/.test(value),
+          "publicKey must be a non-empty, single-line authorized_keys entry",
+        )
         .describe("SSH public key in authorized_keys format (e.g. 'ssh-ed25519 AAAA... user')"),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
publicKey: z
.string()
.describe("SSH public key in authorized_keys format (e.g. 'ssh-ed25519 AAAA... user')"),
}),
publicKey: z
.string()
.refine(
(value) => value.trim().length > 0 && !/[\r\n]/.test(value),
"publicKey must be a non-empty, single-line authorized_keys entry",
)
.describe("SSH public key in authorized_keys format (e.g. 'ssh-ed25519 AAAA... user')"),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-tenki/src/tools.ts` around lines 53 - 56, Update the
publicKey schema in the tool definition to reject whitespace-only values and any
values containing newline characters before updateSshAuthorizedKeys is invoked.
Preserve valid single-line authorized_keys values, and add matching schema cases
covering blank, whitespace-only, and multiline inputs.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

4 issues found across 16 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/sandbox-tenki/src/sandbox.ts">

<violation number="1" location="packages/sandbox-tenki/src/sandbox.ts:734">
P1: A timeout or abort while resuming can permanently wedge this sandbox: the first `execute()` returns, but its unresolved `resumeIfPaused()` remains at the head of `lifecycleTransition`, so all later lifecycle operations wait forever. The lifecycle queue would need cancellation/timeout handling (or a way to detach a canceled transition) before returning the cancellation result.</violation>

<violation number="2" location="packages/sandbox-tenki/src/sandbox.ts:803">
P2: A mid-stream transport failure can silently truncate returned stdout/stderr: partial streamed bytes win over the completed handle's aggregate output. Track stream completion/error and use the aggregate result when a pump fails, while preserving byte-limit truncation.</violation>
</file>

<file name="packages/sandbox-tenki/src/utils.ts">

<violation number="1" location="packages/sandbox-tenki/src/utils.ts:262">
P3: Multiline Tenki `reason` values are appended verbatim, so one diagnostic can inject extra stderr lines despite the documented single-line format. Normalize or escape CR/LF before composing the diagnostic.</violation>
</file>

<file name="website/docs/workspaces/sandbox.md">

<violation number="1" location="website/docs/workspaces/sandbox.md:448">
P3: The website docs only mention `TENKI_API_KEY` as the fallback environment variable, but the Tenki SDK also supports `TENKI_AUTH_TOKEN`. Consider updating the docs to mention both, consistent with the README which says: "The API key defaults to the `TENKI_API_KEY` (or `TENKI_AUTH_TOKEN`) environment variable when `apiKey` is omitted."</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

// does not exist yet, so `requestKill()` would have nothing to kill. This
// is the only await left between the guards and `session.run()`; the
// `runOptions` build below is synchronous.
const resumed = await raceCancellation(this.resumeIfPaused(session));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: A timeout or abort while resuming can permanently wedge this sandbox: the first execute() returns, but its unresolved resumeIfPaused() remains at the head of lifecycleTransition, so all later lifecycle operations wait forever. The lifecycle queue would need cancellation/timeout handling (or a way to detach a canceled transition) before returning the cancellation result.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/sandbox-tenki/src/sandbox.ts, line 734:

<comment>A timeout or abort while resuming can permanently wedge this sandbox: the first `execute()` returns, but its unresolved `resumeIfPaused()` remains at the head of `lifecycleTransition`, so all later lifecycle operations wait forever. The lifecycle queue would need cancellation/timeout handling (or a way to detach a canceled transition) before returning the cancellation result.</comment>

<file context>
@@ -0,0 +1,830 @@
+      // does not exist yet, so `requestKill()` would have nothing to kill. This
+      // is the only await left between the guards and `session.run()`; the
+      // `runOptions` build below is synchronous.
+      const resumed = await raceCancellation(this.resumeIfPaused(session));
+      if (resumed === cancellationMarker) {
+        return cancellationResult();
</file context>

cleanup();
}

const stdoutInfo = resolveOutput(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: A mid-stream transport failure can silently truncate returned stdout/stderr: partial streamed bytes win over the completed handle's aggregate output. Track stream completion/error and use the aggregate result when a pump fails, while preserving byte-limit truncation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/sandbox-tenki/src/sandbox.ts, line 803:

<comment>A mid-stream transport failure can silently truncate returned stdout/stderr: partial streamed bytes win over the completed handle's aggregate output. Track stream completion/error and use the aggregate result when a pump fails, while preserving byte-limit truncation.</comment>

<file context>
@@ -0,0 +1,830 @@
+      cleanup();
+    }
+
+    const stdoutInfo = resolveOutput(
+      stdoutBuffer,
+      result ? decodeBytes(result.stdout) : undefined,
</file context>

// `errno` is a non-optional proto scalar, so it arrives as 0 (not absent)
// whenever the guest agent has nothing to report.
const errno = typeof record.errno === "number" && record.errno !== 0 ? record.errno : undefined;
const rawReason = typeof record.reason === "string" ? record.reason.trim() : "";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: Multiline Tenki reason values are appended verbatim, so one diagnostic can inject extra stderr lines despite the documented single-line format. Normalize or escape CR/LF before composing the diagnostic.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/sandbox-tenki/src/utils.ts, line 262:

<comment>Multiline Tenki `reason` values are appended verbatim, so one diagnostic can inject extra stderr lines despite the documented single-line format. Normalize or escape CR/LF before composing the diagnostic.</comment>

<file context>
@@ -0,0 +1,333 @@
+  // `errno` is a non-optional proto scalar, so it arrives as 0 (not absent)
+  // whenever the guest agent has nothing to report.
+  const errno = typeof record.errno === "number" && record.errno !== 0 ? record.errno : undefined;
+  const rawReason = typeof record.reason === "string" ? record.reason.trim() : "";
+  const reason = BENIGN_RUN_REASONS.has(rawReason.toLowerCase())
+    ? undefined
</file context>

});
```

The API key defaults to the `TENKI_API_KEY` environment variable when omitted. Ordinary workspace API keys infer their workspace scope server-side, so omit `workspaceId` for normal usage. If you use trusted service credentials that can access multiple workspaces, pass `workspaceId` to select one explicitly:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: The website docs only mention TENKI_API_KEY as the fallback environment variable, but the Tenki SDK also supports TENKI_AUTH_TOKEN. Consider updating the docs to mention both, consistent with the README which says: "The API key defaults to the TENKI_API_KEY (or TENKI_AUTH_TOKEN) environment variable when apiKey is omitted."

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At website/docs/workspaces/sandbox.md, line 448:

<comment>The website docs only mention `TENKI_API_KEY` as the fallback environment variable, but the Tenki SDK also supports `TENKI_AUTH_TOKEN`. Consider updating the docs to mention both, consistent with the README which says: "The API key defaults to the `TENKI_API_KEY` (or `TENKI_AUTH_TOKEN`) environment variable when `apiKey` is omitted."</comment>

<file context>
@@ -419,6 +420,131 @@ const workspace = new Workspace({
+});
+```
+
+The API key defaults to the `TENKI_API_KEY` environment variable when omitted. Ordinary workspace API keys infer their workspace scope server-side, so omit `workspaceId` for normal usage. If you use trusted service credentials that can access multiple workspaces, pass `workspaceId` to select one explicitly:
+
+```ts
</file context>
Suggested change
The API key defaults to the `TENKI_API_KEY` environment variable when omitted. Ordinary workspace API keys infer their workspace scope server-side, so omit `workspaceId` for normal usage. If you use trusted service credentials that can access multiple workspaces, pass `workspaceId` to select one explicitly:
The API key defaults to the `TENKI_API_KEY` (or `TENKI_AUTH_TOKEN`) environment variable when omitted. Ordinary workspace API keys infer their workspace scope server-side, so omit `workspaceId` for normal usage. If you use trusted service credentials that can access multiple workspaces, pass `workspaceId` to select one explicitly:

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