Skip to content

feat(cli): support containerized openclaw#52651

Merged
sallyom merged 1 commit intoopenclaw:mainfrom
sallyom:instance-target-cli
Mar 24, 2026
Merged

feat(cli): support containerized openclaw#52651
sallyom merged 1 commit intoopenclaw:mainfrom
sallyom:instance-target-cli

Conversation

@sallyom
Copy link
Copy Markdown
Contributor

@sallyom sallyom commented Mar 23, 2026

Summary

CLI: Add support for containerized OpenClaw

Change Type (select all)

  • Feature

Scope (select all touched areas)

  • Integrations
  • UI / DX

Linked Issue/PR

User-visible / Behavior Changes

List user-visible changes (including defaults/config).
If none, write None.

Security Impact (required)

  • New permissions/capabilities? (Yes)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (Yes)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

This adds host-side CLI support for executing openclaw inside an already-running local Docker/Podman container selected by the operator via --container or OPENCLAW_CONTAINER.
Risk is local-only: the feature uses existing docker exec/podman exec capability and therefore can access whatever config/state the targeted container already has access to. It does not introduce new remote network calls or change secret handling.
Mitigations: execution is limited to explicitly named running containers, --profile/--dev are rejected to avoid ambiguous cross-environment targeting, and the inner CLI is marked with a bypass env var to prevent recursive re-exec loops.

Repro + Verification

Environment

  • OS: MacOS
  • Runtime/container: Podman

Human Verification (required)

What you personally verified (not just CI), and how:

$ openclaw cron status
{
  "enabled": false,
  "storePath": "/home/node/.openclaw/cron/jobs.json",
  "jobs": 1,
  "nextWakeAtMs": null
}
$ openclaw config set cron.enabled true
Updated cron.enabled. Restart the gateway to apply.
$ openclaw gateway restart
Gateway service disabled.
Start with: Restart the container with your runtime, for example: docker restart openclaw-somalley-lobster
$ podman restart openclaw-somalley-lobster
$ openclaw cron status
{
  "enabled": true,
  "storePath": "/home/node/.openclaw/cron/jobs.json",
  "jobs": 1,
  "nextWakeAtMs": 1774244400000
}
$ openclaw cron run does-not-exist; echo $?
GatewayClientRequestError: Error: unknown cron job id: does-not-exist
1
# exit code from container provided

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)

@openclaw-barnacle openclaw-barnacle bot added cli CLI command changes size: L maintainer Maintainer-authored PR labels Mar 23, 2026
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 23, 2026

🔒 Aisle Security Analysis

We found 3 potential security issue(s) in this PR:

# Severity Title
1 🔵 Low Terminal/log injection and unsafe copy-paste command rendering via unsanitized OPENCLAW_CONTAINER_HINT
2 🔵 Low Environment-controlled sudo target user in container runtime detection (OPENCLAW_PODMAN_USER)
3 🔵 Low Option injection into docker/podman commands via untrusted container name (missing -- terminator)
Vulnerabilities

1. 🔵 Terminal/log injection and unsafe copy-paste command rendering via unsanitized OPENCLAW_CONTAINER_HINT

Property Value
Severity Low
CWE CWE-117
Location src/cli/command-format.ts:25-32

Description

Untrusted container names from argv/env (--container, OPENCLAW_CONTAINER, OPENCLAW_CONTAINER_HINT) are interpolated into user-facing strings without any terminal/log sanitization or shell-quoting.

Impacts:

  • Terminal escape / log injection: a container name containing ANSI escape sequences, newlines, carriage returns, or other control characters can forge/mislead terminal output and logs.
  • Unsafe copy/paste shell injection: formatCliCommand() renders command examples with --container ${container} without shell quoting. If a user copies and pastes the rendered string, a container name containing characters like spaces, quotes, backticks, $(), ;, etc. can change the meaning of the pasted shell command.

Vulnerable code (rendering a shell command snippet):

additions.push(`--container ${container}`);

Note: the repo already contains sanitizeTerminalText() in src/terminal/safe-text.ts, but it is not applied here.

Recommendation

Treat container names as untrusted when rendering to terminals/logs and when embedding into copy/paste command examples.

  1. Sanitize for terminal/log output (strip ANSI + control chars, normalize newlines):
import { sanitizeTerminalText } from "../terminal/safe-text.js";

const containerRaw = env.OPENCLAW_CONTAINER_HINT?.trim();
const containerSafe = containerRaw ? sanitizeTerminalText(containerRaw) : undefined;
  1. Shell-quote values when rendering command examples intended for copy/paste (do not rely on sanitization alone). For POSIX shells you can single-quote and escape single quotes:
function shellQuotePosix(value: string): string {
  return `'${value.replace(/'/g, `'\\''`)}'`;
}

additions.push(`--container ${shellQuotePosix(containerRaw)}`);
  1. Apply the same approach anywhere container names are interpolated into hints/errors (e.g., renderGatewayServiceStartHints and thrown errors in container-target.ts).

2. 🔵 Environment-controlled sudo target user in container runtime detection (OPENCLAW_PODMAN_USER)

Property Value
Severity Low
CWE CWE-269
Location src/cli/container-target.ts:136-143

Description

The container-target feature selects candidate container runtimes, including a sudo -u <user> podman ... fallback.

  • The target user is taken directly from the environment variable OPENCLAW_PODMAN_USER.
  • There is no allowlist/validation restricting this to the intended dedicated user (documented as openclaw).
  • If an attacker can influence the environment in which openclaw is executed (wrapper script, compromised service unit, etc.), they can cause the CLI to attempt sudo -u <arbitraryUser> podman ....
  • While spawnSync is used with an argv array (so this is not shell injection), this can still lead to unexpected execution context and may become a privilege escalation in environments with permissive sudoers rules (e.g., NOPASSWD for podman or for switching users).

Vulnerable code:

const podmanUser = env.OPENCLAW_PODMAN_USER?.trim() || "openclaw";
...
candidates.push({
  runtime: "podman",
  command: "sudo",
  argsPrefix: ["-u", podmanUser, "podman"],
});

Recommendation

Do not allow an arbitrary environment variable to choose the sudo target user.

Recommended options (pick one):

  1. Hardcode the intended dedicated user (openclaw) and remove OPENCLAW_PODMAN_USER entirely.

  2. If configurability is required, strictly validate + allowlist the value (e.g., only openclaw), and ignore/abort otherwise.

Example (allowlist to openclaw only):

const configured = env.OPENCLAW_PODMAN_USER?.trim();
const podmanUser = configured ?? "openclaw";

if (configured && configured !== "openclaw") {
  throw new Error("OPENCLAW_PODMAN_USER must be 'openclaw'");
}

Additionally, consider requiring an explicit CLI flag (e.g. --podman-sudo-user openclaw) rather than inheriting from ambient environment variables, and document the security implications if sudo is used.


3. 🔵 Option injection into docker/podman commands via untrusted container name (missing -- terminator)

Property Value
Severity Low
CWE CWE-88
Location src/cli/container-target.ts:113-119

Description

The container target feature passes a user-controlled container identifier (from --container or OPENCLAW_CONTAINER) directly into podman/docker CLI argument lists without an end-of-options terminator (--) or validation.

Because docker/podman use flag parsers that treat tokens starting with - as options while still parsing flags, a container name such as --user=root / --privileged / --config=... can be interpreted as a flag instead of a positional container argument. This can alter the behavior of the invoked command (inspect / exec) and potentially lead to unexpected execution context (different user/env), information disclosure, or executing in an unintended container.

Vulnerable flows:

  • Input: --container <value> parsed in parseCliContainerArgs() and/or OPENCLAW_CONTAINER in resolveCliContainerTarget().
  • Sink: passed as an argument to spawnSync() for inspect and exec.

Vulnerable code (examples):

[...argsPrefix, "inspect", "--format", "{{.State.Running}}", params.containerName]
...,
params.containerName,
"openclaw",
...params.argv,

Recommendation

Prevent option/argument injection by ensuring the container identifier cannot be parsed as flags:

  1. Add an end-of-options terminator before the container name for both inspect and exec invocations.
  2. Optionally validate container names (reject those starting with - and/or not matching allowed characters) to fail fast.

Example fix:

function assertSafeContainerName(name: string) {
  if (name.startsWith("-")) {
    throw new Error("Invalid container name");
  }
}// inspect
assertSafeContainerName(params.containerName);
const args = [
  ...params.exec.argsPrefix,
  "inspect",
  "--format",
  "{{.State.Running}}",
  "--",
  params.containerName,
];// exec
assertSafeContainerName(params.containerName);
return [
  ...params.exec.argsPrefix,
  "exec",
  ...interactiveFlags,
  envFlag,
  `OPENCLAW_CONTAINER_HINT=${params.containerName}`,
  envFlag,
  "OPENCLAW_CLI_CONTAINER_BYPASS=1",
  "--",
  params.containerName,
  "openclaw",
  ...params.argv,
];

Using -- ensures that even if the container name starts with -, it is treated as a positional argument by docker/podman.


Analyzed PR: #52651 at commit 1826117

Last updated on: 2026-03-24T16:22:25Z

@sallyom sallyom force-pushed the instance-target-cli branch from 294f164 to 7f16596 Compare March 23, 2026 05:45
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR adds host-side CLI support for executing openclaw commands inside an already-running Podman/Docker container via --container <name> or OPENCLAW_CONTAINER. The container detection logic (container-target.ts), hint filtering (shared.ts), fast-path guards (entry.ts), and argv-skipping utilities (cli-root-options.ts) are all clean and well-tested in isolation.

One critical bug affects the primary feature path:

  • --container flag does not actually trigger container exec. In runCli (run-main.ts), normalizedArgv is overwritten with parsedProfile.argv, which is derived from parsedContainer.argv — the argv that parseCliContainerArgs already stripped --container <name> from. When maybeRunCliInContainer is then called with this stripped argv, its internal re-parse finds container: null and the OPENCLAW_CONTAINER env-var fallback is the only path that works. Flag-based invocations like openclaw --container demo status silently fall through and run locally. The test in run-main.exit.test.ts that is meant to cover this mocks parseCliContainerArgs as an identity no-op, so it never catches the real-world stripping.

Additional notes:

  • takeValue in container-target.ts accepts an adjacent flag (e.g. --no-color) as a container name, inconsistently with isValueToken in cli-root-options.ts which rejects flags as values.
  • resolveCliContainerTarget is called redundantly on line 89 of run-main.ts; the result of the already-called parseCliContainerArgs on line 90 could supply the same information without a second parse.

Confidence Score: 2/5

  • Not safe to merge — the primary user-facing feature (--container flag) is broken in the real execution path due to pre-stripping of argv before maybeRunCliInContainer is called.
  • The feature works only via OPENCLAW_CONTAINER env var; the --container CLI flag, which is explicitly documented and tested, silently falls through to local execution. The masking of this bug by the test mock compounds the issue. The overall structure and peripheral code (hints, fast-path guards, argv utilities) are solid, but the core dispatch in run-main.ts needs to be fixed before merge.
  • src/cli/run-main.ts (critical dispatch bug) and src/cli/run-main.exit.test.ts (test masks the bug via mock)
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/cli/run-main.ts
Line: 87-113

Comment:
**`--container` flag is silently dropped before container exec fires**

`normalizedArgv` is reassigned to `parsedProfile.argv` (line 104), which is derived from `parsedContainer.argv` — the argv that already has `--container <name>` stripped out. When `maybeRunCliInContainer` is called on line 106 with the stripped `normalizedArgv`, its internal `parseCliContainerArgs` call returns `container: null`, and the `resolveCliContainerTarget` fallback only finds a container name if `OPENCLAW_CONTAINER` is set in the environment. With a flag-only invocation like `openclaw --container demo status`, `containerName` resolves to `null` and `maybeRunCliInContainer` returns `{ handled: false }` — the exec never fires.

The existing test in `run-main.exit.test.ts` masks this because `parseCliContainerArgs` is mocked there to be a no-op identity function, so `normalizedArgv` still contains `--container demo` at the point `maybeRunCliInContainer` is called. In production the real `parseCliContainerArgs` strips the flag first.

A minimal fix is to call `maybeRunCliInContainer` with the pre-stripping argv (before `normalizedArgv` is overwritten), passing the container-stripped inner argv a different way, or by having `maybeRunCliInContainer` accept an explicit `containerName` override. For example:

```typescript
// call maybeRunCliInContainer BEFORE stripping --container
const containerTarget = maybeRunCliInContainer(normalizedArgv);
if (containerTarget.handled) {
  if (containerTarget.exitCode !== 0) {
    process.exitCode = containerTarget.exitCode;
  }
  return;
}
// now strip --container (already done internally) and strip --profile
normalizedArgv = containerTarget.argv;   // maybeRunCliInContainer already returns the stripped argv
```

The stripping that matters for the inner command is already done inside `buildContainerExecArgs` via `parsed.argv.slice(2)`, so `maybeRunCliInContainer` just needs to receive the original argv with `--container` still present.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/cli/container-target.ts
Line: 16-30

Comment:
**`--container` without `=` consumes adjacent flags as its value**

`takeValue` accepts any non-empty string as the flag's value, including other flags like `--no-color`:

```
openclaw --container --no-color status
```

Here `next = "--no-color"`, `(next ?? "").trim()` is truthy → `consumedNext: true`, container name becomes `"--no-color"`, and `--no-color` is swallowed from `out`. This is inconsistent with `consumeRootOptionToken` in `cli-root-options.ts`, which uses `isValueToken` (which explicitly returns `false` for any arg starting with `-`) to decide whether the adjacent token is a value.

Consider aligning `takeValue` with `isValueToken` so that `--container --no-color` is treated the same way in both parsers:

```typescript
function takeValue(raw: string, next: string | undefined) {
  if (raw.includes("=")) {
    const [, value] = raw.split("=", 2);
    const trimmed = (value ?? "").trim();
    return { value: trimmed || null, consumedNext: false };
  }
  // Mirror cli-root-options: a following flag is not a value
  const isValue = !!next && next !== "--" && !next.startsWith("-");
  return { value: isValue ? next!.trim() || null : null, consumedNext: isValue };
}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/cli/run-main.ts
Line: 89-93

Comment:
**Redundant double-parse of argv**

`resolveCliContainerTarget` on line 89 internally calls `parseCliContainerArgs`, and then `parseCliContainerArgs` is called again explicitly on line 90 with the same `normalizedArgv`. The second call is needed to get the stripped argv, but the result of the first could be reused. Consider extracting a single parse:

```typescript
const parsedContainer = parseCliContainerArgs(normalizedArgv);
if (!parsedContainer.ok) {
  throw new Error(parsedContainer.error);
}
const containerTargetName =
  parsedContainer.container ?? process.env.OPENCLAW_CONTAINER?.trim() ?? null;
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(cli): support targeting running con..." | Re-trigger Greptile

Copy link
Copy Markdown

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

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: 294f1643f6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 7f16596 to 7ddb35c Compare March 23, 2026 05:50
Copy link
Copy Markdown

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

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: 7ddb35c055

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 7ddb35c to 372cd55 Compare March 23, 2026 06:07
Copy link
Copy Markdown

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

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: 372cd5539a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 372cd55 to cff5384 Compare March 23, 2026 12:20
Copy link
Copy Markdown

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

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: cff5384fa4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from cff5384 to 9fb6a2c Compare March 23, 2026 18:55
Copy link
Copy Markdown

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

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: 9fb6a2c29c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 9fb6a2c to 0eb55b5 Compare March 23, 2026 19:18
Copy link
Copy Markdown

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

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: 0eb55b5a65

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 0eb55b5 to 23f4156 Compare March 23, 2026 19:43
Copy link
Copy Markdown

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

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: 23f415617e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

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

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: 397bc87978

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 397bc87 to 0edf53e Compare March 23, 2026 20:39
Copy link
Copy Markdown

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

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: 0edf53e593

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch 3 times, most recently from 2b37fad to b5f7081 Compare March 23, 2026 21:17
@sallyom
Copy link
Copy Markdown
Contributor Author

sallyom commented Mar 23, 2026

Removed the .dotenv handling that contributed to security warning above, it's not needed for this feature.

@sallyom sallyom force-pushed the instance-target-cli branch from 297b14a to 4c9abe4 Compare March 23, 2026 22:15
Copy link
Copy Markdown

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

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: 4c9abe495a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 4c9abe4 to 173d2f0 Compare March 23, 2026 23:15
Copy link
Copy Markdown

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

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: 173d2f091c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 173d2f0 to 3b38c6b Compare March 24, 2026 01:24
Copy link
Copy Markdown

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

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: 3b38c6b56e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 3b38c6b to 4578e5d Compare March 24, 2026 01:44
Copy link
Copy Markdown

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

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: 4578e5d5c5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 4578e5d to 8797afc Compare March 24, 2026 03:18
Copy link
Copy Markdown

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

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: 8797afcdca

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from 8797afc to d9f2dee Compare March 24, 2026 04:19
Copy link
Copy Markdown

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

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: d9f2dee865

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@sallyom sallyom force-pushed the instance-target-cli branch from d9f2dee to d117983 Compare March 24, 2026 04:34
Copy link
Copy Markdown

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

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: d1179832ec

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +163 to +167
for (const exec of candidates) {
if (
isContainerRunning({
exec,
containerName: params.containerName,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Stop probing sudo Podman after Docker already matches

resolveRunningContainer() keeps probing every candidate even after Docker has already matched as running. Since candidateContainerRuntimes() appends sudo -u <OPENCLAW_PODMAN_USER> podman for most users (USER != openclaw), a normal Docker flow (openclaw --container demo ...) still executes a sudo inspect probe, which can prompt for a password (or block/fail in non-interactive shells) before docker exec runs. This makes container-targeted commands unreliable in common scripted Docker environments; once Docker is the only match, the resolver should not run the dedicated-user sudo probe.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed - chose to stop after matching docker rather than probing for extreme edge case of podman, docker containers both existing on same system with same container name.

@sallyom sallyom force-pushed the instance-target-cli branch from d117983 to 4a84f97 Compare March 24, 2026 13:04
Copy link
Copy Markdown

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

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: 4a84f97c57

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +172 to +173
if (exec.runtime === "docker") {
break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Detect duplicate runtimes even after Docker matches

Fresh evidence: resolveRunningContainer() still breaks out of probing as soon as Docker returns running, so the later dedicated-user Podman candidate (sudo -u <user> podman) is never checked. In environments where both Docker and dedicated-user Podman have a container with the same name, openclaw --container <name> ... will silently target Docker instead of surfacing the intended ambiguity error, which can run commands against the wrong deployment.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

chose to stop probing after docker match since it's a non-issue/rare that there would be a container of the same name on same system under both podman & docker.

@sallyom sallyom force-pushed the instance-target-cli branch 2 times, most recently from 4529bc7 to a72456e Compare March 24, 2026 13:24
Copy link
Copy Markdown

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

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: a72456e1fc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +97 to +101
parsedContainer.container ?? process.env.OPENCLAW_CONTAINER?.trim() ?? null;
if (
containerTargetName &&
(parsedProfile.profile ||
process.env.OPENCLAW_PROFILE?.trim() ||
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Ignore inherited OPENCLAW_CONTAINER in bypassed child CLI

Fresh evidence: maybeRunCliInContainer() still spawns docker/podman exec with env: resolvedDeps.env unchanged, so an env-based invocation (OPENCLAW_CONTAINER=... openclaw ...) makes the inner process inherit OPENCLAW_CONTAINER plus OPENCLAW_CLI_CONTAINER_BYPASS=1. This guard then treats the bypassed child as still container-targeted and can fail before command execution when the container environment sets gateway/auth vars (for example OPENCLAW_GATEWAY_TOKEN), even though the outer invocation had already passed validation. In practice this makes OPENCLAW_CONTAINER mode brittle/unusable in common tokenized container setups; the child path should ignore OPENCLAW_CONTAINER when bypass is set (or the parent should clear it before exec).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed!

@sallyom sallyom force-pushed the instance-target-cli branch from a72456e to 1826117 Compare March 24, 2026 14:03
@sallyom
Copy link
Copy Markdown
Contributor Author

sallyom commented Mar 24, 2026

ci errors unrelated

@sallyom sallyom merged commit 91adc5e into openclaw:main Mar 24, 2026
35 of 37 checks passed
tiagonix pushed a commit to tiagonix/openclaw that referenced this pull request Mar 24, 2026
siofra-seksbot added a commit to TheBotsters/botster-ego that referenced this pull request Mar 25, 2026
* Formatting fixes and remove trailing dash acceptance

* Remove lower casing -- preserving prior behavior

* fix: preserve legacy clawhub skill updates (openclaw#53206) (thanks @drobison00)

* feat(csp): support inline script hashes in Control UI CSP (openclaw#53307) thanks @BunsDev

Co-authored-by: BunsDev <[email protected]>
Co-authored-by: Nova <[email protected]>

* refactor: separate exec policy and execution targets

* test: print failed test lane output tails

* fix(cron): make --tz work with --at for one-shot jobs

Previously, `--at` with an offset-less ISO datetime (e.g. `2026-03-23T23:00:00`)
was always interpreted as UTC, even when `--tz` was provided. This caused one-shot
jobs to fire at the wrong time.

Changes:
- `parseAt()` now accepts an optional `tz` parameter
- When `--tz` is provided with `--at`, offset-less datetimes are interpreted in
  that IANA timezone using Intl.DateTimeFormat
- Datetimes with explicit offsets (e.g. `+01:00`, `Z`) are unaffected
- Removed the guard in cron-edit that blocked `--tz` with `--at`
- Updated `--at` help text to mention `--tz` support
- Added 2 tests verifying timezone resolution and offset preservation

* fix: land cron tz one-shot handling and prerelease config warnings (openclaw#53224) (thanks @RolfHegr)

* fix: clean changelog merge duplication (openclaw#53224) (thanks @RolfHegr)

* test: isolate line jiti runtime smoke

* refactor: harden extension runtime-api seams

* tests: improve boundary audit coverage and safety (openclaw#53080)

* tools: extend seam audit inventory

* tools: tighten seam audit heuristics

* tools: refine seam test matching

* tools: refine seam audit review heuristics

* style: format seam audit script

* tools: widen seam audit matcher coverage

* tools: harden seam audit coverage

* tools: tighten boundary audit matchers

* tools: ignore mocked import matches in boundary audit

* test: include native command reply seams in audit

* fix: command auth SecretRef resolution (openclaw#52791) (thanks @Lukavyi)

* fix(command-auth): handle unresolved SecretRef in resolveAllowFrom

* fix(command-auth): fall back to config allowlists

* fix(command-auth): avoid duplicate resolution fallback

* fix(command-auth): fail closed on invalid allowlists

* fix(command-auth): isolate fallback resolution errors

* fix: record command auth SecretRef landing notes (openclaw#52791) (thanks @Lukavyi)

---------

Co-authored-by: Ayaan Zaidi <[email protected]>

* refactor: extract cron schedule and test runner helpers

* fix: populate currentThreadTs in threading tool context fallback for Telegram DM topics (openclaw#52217)

When a channel plugin lacks a custom buildToolContext (e.g. Telegram),
the fallback path in buildThreadingToolContext did not set currentThreadTs
from the inbound MessageThreadId. This caused resolveTelegramAutoThreadId
to return undefined, so message tool sends without explicit threadId
would route to the main chat instead of the originating DM topic.

Fixes openclaw#52217

* fix: unblock runtime-api smoke checks

* refactor: split tracked ClawHub update flows

* build: prepare 2026.3.23-2

* fix: preserve command auth resolution errors on empty inferred allowlists

* docs: refresh plugin-sdk api baseline

* test: harden linux runtime smoke guards

* fix(runtime): anchor bundled plugin npm staging to active node

* tests: cron coverage and NO_REPLY delivery fixes (openclaw#53366)

* tools: extend seam audit inventory

* tools: audit cron seam coverage gaps

* test: add cron seam coverage tests

* fix: avoid marking NO_REPLY cron deliveries as delivered

* fix: clean up delete-after-run NO_REPLY cron sessions

* fix: verify global npm correction installs

* build: prepare 2026.3.24

* docs: update mac release automation guidance

* fix: fail closed when provider inference drops errored allowlists

* fix: reject nonexistent zoned cron at-times

* fix: hash inline scripts with data-src attributes

* ci: balance shards and reuse pr artifacts

* refactor: simplify provider inference and zoned parsing helpers

* fix: unify live model auth gating

* tests: add boundary coverage for media delivery (openclaw#53361)

* tests: add boundary coverage for media delivery

* tests: isolate telegram outbound adapter transport

* tests: harden telegram webhook certificate assertion

* tests: fix guardrail false positives on rebased branch

* msteams: extract structured quote/reply context (openclaw#51647)

* msteams: extract structured quote/reply context from Teams HTML attachments

* msteams: address PR openclaw#51647 review feedback

* msteams: add message edit and delete support (openclaw#49925)

- Add edit/delete action handlers with toolContext.currentChannelId
  fallback for in-thread edits/deletes without explicit target
- Add editMessageMSTeams/deleteMessageMSTeams to channel runtime
- Add updateActivity/deleteActivity to SendContext and MSTeamsTurnContext
- Extend content param with text/content/message fallback chain
- Update test mocks for new SendContext shape

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>

* fix(doctor): honor --fix in non-interactive mode

Ensure repair-mode doctor prompts auto-accept recommended fixes even when running non-interactively, while still requiring --force for aggressive rewrites.

This restores the expected behavior for upgrade/doctor flows that rely on 'openclaw doctor --fix --non-interactive' to repair stale gateway service configuration such as entrypoint drift after global updates.

Co-authored-by: Copilot <[email protected]>

* Preserve no-restart during update doctor fixes

Co-authored-by: Copilot <[email protected]>

* fix(doctor): skip service config repairs during updates

Co-authored-by: Copilot <[email protected]>

* fix: add config clobber forensics

* fix(ui): resolve model provider from catalog instead of stale session default

When the server returns a bare model name (e.g. "deepseek-chat") with
a session-level modelProvider (e.g. "zai"), the UI blindly prepends
the provider — producing "zai/deepseek-chat" instead of the correct
"deepseek/deepseek-chat". This causes "model not allowed" errors
when switching between models from different providers.

Root cause: resolveModelOverrideValue() and resolveDefaultModelValue()
in app-render.helpers.ts, plus the /model slash command handler in
slash-command-executor.ts, all call resolveServerChatModelValue()
which trusts the session's default provider. The session provider
reflects the PREVIOUS model, not the newly selected one.

Fix: for bare model names, create a raw ChatModelOverride and resolve
through normalizeChatModelOverrideValue() which looks up the correct
provider from the model catalog. Falls back to server-provided provider
only if the catalog lookup fails. All 3 call sites are fixed.

Closes openclaw#53031

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: HCL <[email protected]>

* style(ui): polish agent file preview and usage popovers (openclaw#53382)

* feat: make workspace links clickable in agent context card and files list

Updated the agent context card and files list to render workspace names as clickable links, allowing users to easily access the corresponding workspace files. This enhances usability by providing direct navigation to the workspace location.

* style(ui): polish markdown preview dialog

* style(ui): reduce markdown preview list indentation

* style(ui): update markdown preview dialog width and alignment

* fix(ui): open usage filter popovers toward the right

* style(ui): adjust positioning of usage filter and export popovers

* style(ui): update sidebar footer padding and modify usage header z-index

* style(ui): adjust positioning of usage filter popover to the left and export popover to the right

* style(ui): simplify workspace link rendering in agent context card

* UI: make workspace paths interactive buttons or plain text

Agent Context card workspace (Channels/Cron panels): replace non-interactive
<div> with a real <button> wired to onSelectPanel('files'), matching the
Overview panel pattern.

Core Files footer workspace: drop workspace-link class since the user is
already on the Files panel — keep as plain text.

* fix(agents): suppress heartbeat prompt for cron-triggered embedded runs

Prevent cron-triggered embedded runs from inheriting the default heartbeat prompt so non-cron session targets stop reading HEARTBEAT.md and polluting scheduled turns.

Made-with: Cursor

* test(agents): cover additional heartbeat prompt triggers

Document that default-agent heartbeat prompt injection still applies to memory-triggered and triggerless runs while cron remains excluded.

Made-with: Cursor

* fix: land cron heartbeat prompt suppression (openclaw#53152) (thanks @Protocol-zero-0)

* msteams: implement Teams AI agent UX best practices (openclaw#51808)

Migrates the Teams extension from @microsoft/agents-hosting to the official Teams SDK (@microsoft/teams.apps + @microsoft/teams.api) and implements Microsoft's AI UX best practices for Teams agents.

- AI-generated label on all bot messages (Teams native badge + thumbs up/down)
- Streaming responses in 1:1 chats via Teams streaminfo protocol
- Welcome card with configurable prompt starters on bot install
- Feedback with reflective learning (negative feedback triggers background reflection)
- Typing indicators for personal + group chats (disabled for channels)
- Informative status updates (progress bar while LLM processes)
- JWT validation via Teams SDK createServiceTokenValidator
- User-Agent: teams.ts[apps]/<sdk-version> OpenClaw/<version> on outbound requests
- Fix copy-pasted image downloads (smba.trafficmanager.net auth allowlist)
- Pre-parse auth gate (reject unauthenticated requests before body parsing)
- Reflection dispatcher lifecycle fix (prevent leaked dispatchers)
- Colon-safe session filenames (Windows compatibility)
- Cooldown cache eviction (prevent unbounded memory growth)

Closes openclaw#51806

* refactor: tighten embedded prompt and sidecar guards

* test: audit subagent seam coverage inventory

* test: add exact-stem subagent seam tests

* refactor: clarify doctor repair flow

* fix(plugins): make Matrix recovery paths tolerate stale plugin config (openclaw#52899)

* fix(plugins): address review feedback for Matrix recovery paths (openclaw#52899)

1. Narrow loadConfigForInstall() to catch only INVALID_CONFIG errors,
   letting real failures (fs permission, OOM) propagate.
2. Assert allow array is properly cleaned in stale-cleanup test.
3. Add comment clarifying version-resolution is already addressed via
   the shared VERSION constant.
4. Run cleanStaleMatrixPluginConfig() during install so
   persistPluginInstall() → writeConfigFile() does not fail validation
   on stale Matrix load paths.

* fix(plugins): address review feedback for Matrix recovery paths (openclaw#52899)

* fix: fetch model catalog for slash command updates

* fix: restore teams sdk adapter contracts

* fix: keep slash command model qualification on rebase

* fix: clear production dependency advisories

* fix: delete subagent runs after announce give-up

* refactor: polish trigger and manifest seams

* refactor(ui): extract chat model resolution state

* fix(feishu): preserve docx block tree order (openclaw#40524)

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm vitest run extensions/feishu/src/docx.test.ts

Co-authored-by: Tao Xie <[email protected]>

* fix: stabilize matrix and teams ci assertions

* fix: preserve subagent ended hooks until runtime init

* test: prune low-signal live model sweeps

* test: harden parallels smoke harness

* fix: preserve direct subagent dispatch failures on abort

* fix: report dropped subagent announce queue deliveries

* fix: unblock live harness provider discovery

* fix: finalize resumed subagent cleanup give-ups

* refactor: centralize plugin install config policy

* fix: format subagent registry test

* fix: finalize deferred subagent expiry cleanup

* fix(tui): preserve user message during slow model responses (openclaw#53115)

When a local run ends with an empty final event while another run is active,
skip history reload to prevent clearing the user's pending message from the
chat log. This fixes the 'message disappears' issue with slow models like Ollama.

* fix: preserve deferred TUI history sync (openclaw#53130) (thanks @joelnishanth)

* test: sync app chat model override expectation

* feat(ui): Control UI polish — skills revamp, markdown preview, agent workspace, macOS config tree (openclaw#53411) thanks @BunsDev

Co-authored-by: BunsDev <[email protected]>
Co-authored-by: Nova <[email protected]>

* fix(security): resolve Aisle findings — skill installer validation, terminal sanitization, URL scheme allowlisting (openclaw#53471) thanks @BunsDev

Co-authored-by: BunsDev <[email protected]>
Co-authored-by: Nova <[email protected]>

* fix: widen installer regex allowlists and deduplicate safeExternalHref calls

- SAFE_GO_MODULE: allow uppercase in module paths (A-Z)
- SAFE_BREW_FORMULA: allow @ for versioned formulas ([email protected])
- SAFE_UV_PACKAGE: allow extras [standard] and equality pins ==
- Cache safeExternalHref result in skills detail API key section

* docs: update CONTRIBUTING.md

* test: continue vitest threads migration

* test: continue vitest threads migration

* test: harden threaded shared-worker suites

* test: harden threaded channel follow-ups

* test: defer slack bolt interop for helper-only suites

* fix(agents): harden edit tool recovery (openclaw#52516)

Merged via squash.

Prepared head SHA: e23bde8
Co-authored-by: mbelinky <[email protected]>
Co-authored-by: mbelinky <[email protected]>
Reviewed-by: @mbelinky

* fix(docs): correct json55 typo to json5 in IRC channel docs (openclaw#50831) (openclaw#50842)

Merged via squash.

Prepared head SHA: 0f743bf
Co-authored-by: Hollychou924 <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Reviewed-by: @altaywtf

* fix(secrets): prevent unresolved SecretRef from crashing embedded agent runs

Root cause: Telegram channel monitor captures config at startup before secrets
are resolved and passes it as configOverride into the reply pipeline. Since
getReplyFromConfig() uses configOverride directly (skipping loadConfig() which
reads the resolved runtime snapshot), the unresolved SecretRef objects propagate
into FollowupRun.run.config and crash runEmbeddedPiAgent().

Fix (defense in depth):
- get-reply.ts: detect unresolved SecretRefs in configOverride and fall back to
  loadConfig() which returns the resolved runtime snapshot
- message-tool.ts: try-catch around schema/description building at tool creation
  time so channel discovery errors don't crash the agent
- message-tool.ts: detect unresolved SecretRefs in pre-bound config at tool
  execution time and fall back to gateway secret resolution

Fixes: openclaw#45838

* fix: merge explicit reply config overrides onto fresh config

* fix: clean up failed non-thread subagent spawns

* fix: initialize plugins before killed subagent hooks

* fix: report qmd status counts from real qmd manager (openclaw#53683) (thanks @neeravmakwana)

* fix(memory): report qmd status counts from index

* fix(memory): reuse full qmd manager for status

* fix(memory): harden qmd status manager lifecycle

* fix: ci

* fix: finalize killed delete-mode subagent cleanup

* fix: clean up attachments for killed subagent runs

* feat(cli): support targeting running containerized openclaw instances (openclaw#52651)

Signed-off-by: sallyom <[email protected]>

* fix: ci

* Telegram: recover General topic bindings (openclaw#53699)

Merged via squash.

Prepared head SHA: 546f0c8
Co-authored-by: huntharo <[email protected]>
Co-authored-by: huntharo <[email protected]>
Reviewed-by: @huntharo

* fix: clean up attachments for released subagent runs

* fix(ci): do not cancel in-progress main runs

* fix: clean up attachments for orphaned subagent runs

* test: speed up discord extension suites

* test: speed up slack extension suites

* test: speed up telegram extension suites

* test: speed up whatsapp and shared test suites

* fix(ci): do not cancel in-progress bun runs on main

* fix: clean up attachments when replacing subagent runs

* feat(discord): add autoThreadName 'generated' strategy (openclaw#43366)

* feat(discord): add autoThreadName 'generated' strategy

Adds async thread title generation for auto-created threads:
- autoThread: boolean - enables/disables auto-threading
- autoThreadName: 'message' | 'generated' - naming strategy
- 'generated' uses LLM to create concise 3-6 word titles
- Includes channel name/description context for better titles
- 10s timeout with graceful fallback

* Discord: support non-key auth for generated thread titles

* Discord: skip fallback auto-thread rename

* Discord: normalize generated thread title first content line

* Discord: split thread title generation helpers

* Discord: tidy thread title generation constants and order

* Discord: use runtime fallback model resolution for thread titles

* Discord: resolve thread-title model aliases

* Discord: fallback thread-title model selection to runtime defaults

* Agents: centralize simple completion runtime

* fix(discord): pass apiKey to complete() for thread title generation

The setRuntimeApiKey approach only works for full agent runs that use
authStorage.getApiKey(). The pi-ai complete() function expects apiKey
directly in options or falls back to env vars — it doesn't read from
authStorage.runtimeOverrides.

Fixes thread title generation for Claude/Anthropic users.

* fix(agents): return exchanged Copilot token from prepareSimpleCompletionModel

The recent thread-title fix (3346ba6) passes prepared.auth.apiKey to
complete(). For github-copilot, this was still the raw GitHub token
rather than the exchanged runtime token, causing auth failures.

Now setRuntimeApiKeyForCompletion returns the resolved token and
prepareSimpleCompletionModel includes it in auth.apiKey, so both the
authStorage path and direct apiKey pass-through work correctly.

* fix(agents): catch auth lookup exceptions in completion model prep

getApiKeyForModel can throw for credential issues (missing profile, etc).
Wrap in try/catch to return { error } for fail-soft handling rather than
propagating rejected promises to callers like thread title generation.

* Discord: strip markdown wrappers from generated thread titles

* Discord/agents: align thread-title model and local no-auth completion headers

* Tests: import fresh modules for mocked thread-title/simple-completion suites

* Agents: apply exchanged Copilot baseUrl in simple completions

* Discord: route thread runtime imports through plugin SDK

* Lockfile: add Discord pi-ai runtime dependency

* Lockfile: regenerate Discord pi-ai runtime dependency entries

* Agents: use published Copilot token runtime module

* Discord: refresh config baseline and lockfile

* Tests: split extension runs by isolation

* Discord: add changelog for generated thread titles (openclaw#43366) (thanks @davidguttman)

---------

Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>

* add missing autoArchiveDuration to DiscordGuildChannelConfig type (openclaw#43427)

* add missing autoArchiveDuration to DiscordGuildChannelConfig type

The autoArchiveDuration field is present in the Zod schema
(DiscordGuildChannelSchema) and actively used at runtime in
threading.ts and allow-list.ts, but was missing from the
canonical TypeScript type definition.

Add autoArchiveDuration to DiscordGuildChannelConfig to align
the type with the schema and runtime usage.

* Discord: add changelog for config type fix (openclaw#43427) (thanks @davidguttman)

---------

Co-authored-by: Onur Solmaz <[email protected]>

* refactor: dedupe test and script helpers

* test: speed up discord extension suites

* test: speed up slack extension suites

* test: speed up telegram extension suites

* test: speed up signal and whatsapp extension suites

* fix(discord): avoid bundling pi-ai runtime deps

* fix(lockfile): sync discord dependency removal

* test: speed up discord slack telegram suites

* test: speed up whatsapp and signal suites

* test: speed up google and twitch suites

* test: speed up core unit suites

* fix: preserve cleanup hooks after subagent register failure

* fix: preserve session cleanup hooks after subagent announce

* Feishu: avoid CLI startup failure on unresolved SecretRef

* fix(doctor): add missing baseUrl and models when migrating nano-banana apiKey to google provider

The legacy nano-banana-pro skill migration moves the Gemini API key to
models.providers.google.apiKey but does not populate the required baseUrl
and models fields on the provider entry. When the google provider object
is freshly created (no pre-existing config), the resulting config fails
Zod validation on write:

  Config validation failed: models.providers.google.baseUrl:
  Invalid input: expected string, received undefined

Fix: default baseUrl to 'https://generativelanguage.googleapis.com' and
models to [] when they are not already set, matching the defaults used
elsewhere in the codebase (embeddings-gemini, pdf-native-providers).

Fixes the 'doctor --fix' crash for users who only have a legacy
nano-banana-pro skill entry and no existing models.providers.google.

* fix: use v1beta for migrated google nano banana provider (openclaw#53757) (thanks @mahopan)

* docs: add changelog for PR openclaw#53675 (thanks @hpt)

* fix(msteams): harden feedback reflection follow-ups

* test: stabilize preaction process title assertion (openclaw#53808)

Regeneration-Prompt: |
  Current origin/main fails src/cli/program/preaction.test.ts because the
  test asserts on process.title directly inside Vitest, where that runtime
  interaction is not stable enough to observe the write reliably. Keep the
  production preaction behavior unchanged. Make the test verify that the
  hook assigns the expected title by wrapping process.title with a local
  getter/setter during each test and restoring the original descriptor
  afterward so other tests keep the real process object behavior.

* fix(auth): protect fresher codex reauth state

- invalidate cached Codex CLI credentials when auth.json changes within the TTL window
- skip external CLI sync when the stored Codex OAuth credential is newer
- cover both behaviors with focused regression tests

Refs openclaw#53466

Co-authored-by: Copilot <[email protected]>

* fix: return structured errors for subagent control send failures

* refactor: centralize google API base URL handling

* refactor(msteams): split reply and reflection helpers

* refactor(auth): unify external CLI credential sync

* refactor: split feishu runtime and inspect secret resolution

* test(memory): clear browser and plugin caches between cases

* fix(types): add workspace module shims

* fix: avoid duplicate orphaned subagent resumes

* test(memory): enable lower-interval heap snapshots

* fix: audit clobbered config reads

* fix(whatsapp): filter fromMe messages in groups to prevent infinite loop (openclaw#53386)

* fix: suppress only recent whatsapp group echoes (openclaw#53624) (thanks @w-sss)

* test: speed up slack and telegram suites

* test: speed up cli and model command suites

* test: speed up command runtime suites

* test: speed up backup and doctor suites

* fix(memory): avoid caching status-only managers

* fix: stabilize logging config imports

* fix(slack): improve interactive reply parity (openclaw#53389)

* fix(slack): improve interactive reply parity

* fix(slack): isolate reply interactions from plugins

* docs(changelog): note slack interactive parity fixes

* fix(slack): preserve preview text for local agent replies

* fix(agent): preserve directive text in local previews

* test: preserve child_process exports in restart bun mock

* fix(memory): avoid caching qmd status managers

* test: speed up browser and gateway suites

* test: speed up media fetch suite

* fix(acp): deliver final result text as fallback when no blocks routed

- Check routedCounts.final to detect prior delivery
- Skip fallback for ttsMode='all' to avoid duplicate TTS processing
- Use delivery.deliver for proper routing in cross-provider turns
- Fixes openclaw#46814 where ACP child run results were not delivered

* fix: tighten ACP final fallback semantics (openclaw#53692) (thanks @w-sss)

* fix: unify pi runner usage snapshot fallback

* refactor: isolate ACP final delivery flow

* fix(ci): stop dropping pending main workflow runs

* test(memory): isolate new unit hotspot files

* test(memory): isolate browser remote-tab hotspot

* test(memory): isolate plugin-core hotspot

* test(memory): isolate telegram bot hotspot

* fix: continue subagent kill after session store write failures

* test(memory): isolate telegram fetch hotspot

* test: speed up plugin-sdk and cron suites

* test: speed up browser suites

* test(memory): isolate telegram monitor hotspot

* test(memory): isolate slack action-runtime hotspot

* test(memory): recycle shared channels batches

* fix: fail closed when subagent steer remap fails

* Providers: fix kimi-coding thinking normalization

* Providers: fix kimi fallback normalization

* Plugins: resolve sdk aliases from the running CLI

* Plugins: trust only startup cli sdk roots

* Plugins: sanitize sdk export subpaths

* Webchat: handle bare /compact as session compaction

* Chat UI: tighten compact transport handling

* Chat UI: guard compact retries

* fix: ignore stale subagent steer targets

* fix(discord): notify user on discord when inbound worker times out (openclaw#53823)

* fix(discord): notify user on discord when inbound worker times out.

* fix(discord): notify user on discord when inbound worker times out.

* Discord: await timeout fallback reply

* Discord: add changelog for timeout reply fix (openclaw#53823) (thanks @Kimbo7870)

---------

Co-authored-by: VioGarden <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>

* refactor(channels): route registry lookups through runtime

* refactor(plugins): make runtime registry lazy

* refactor(plugins): make hook runner global lazy

* refactor(plugins): make command registry lazy

* fix: allow compact retry after failed session compaction (openclaw#53875)

* refactor(gateway): make plugin fallback state lazy

* refactor(plugins): make interactive state lazy

* fix(memory): align status manager concurrency test

* fix(runtime): stabilize dist runtime artifacts (openclaw#53855)

* fix(build): stabilize lazy runtime entry paths

* fix(runtime): harden bundled plugin npm staging

* docs(changelog): note runtime artifact fixes

* fix(runtime): stop trusting npm_execpath

* fix(runtime): harden Windows npm staging

* fix(runtime): add safe Windows npm fallback

* ci: start required checks earlier (openclaw#53844)

* ci: start required checks earlier

* ci: restore pnpm in security-fast

* ci: skip docs-only payloads in early check jobs

* ci: harden untrusted pull request execution

* ci: pin gradle setup action

* ci: normalize pull request concurrency cancellation

* ci: remove duplicate early-lane setup

* ci: keep install-smoke push runs unique

* fix: unblock supervisor and memory gate failures

* test: stabilize low-profile parallel gate

* refactor(core): make event and queue state lazy

* fix(ci): refresh plugin sdk baseline and formatting

* chore: refresh plugin sdk api baseline

* fix: ignore stale subagent kill targets

* perf(plugins): scope web search plugin loads

* fix: ignore stale subagent send targets

* fix: validate agent workspace paths before writing identity files (openclaw#53882)

* fix: validate agent workspace paths before writing identity files

* Feedback updates and formatting fixes

* refactor: dedupe tests and harden suite isolation

* test: fix manifest registry fixture typing

* fix: ignore stale bulk subagent kill targets

* fix(cli): precompute bare root help startup path

* fix(test): stabilize npm runner path assertion

* test(gateway): align safe open error code

* test: speed up targeted unit suites

* fix: prefer current subagent targets over stale rows

* fix(ci): use target-platform npm path semantics

* Adjust CLI backend environment handling before spawn (openclaw#53921)

security(agents): sanitize CLI backend env overrides before spawn

* fix: surface finished subagent send targets

* perf(memory): avoid eager provider init on empty search

* fix(test): satisfy cli backend config typing

* fix: let subagent kill cascade through ended parents

* perf(sqlite): use existence probes for empty memory search

* fix: allow follow-up sends to finished subagents

* fix: steer ended subagent orchestrators with live descendants

* test: speed up browser pw-tools-core suites

* test: speed up memory and secrets suites

* fix(ci): align lazy memory provider tests

* fix(test): stabilize memory vector dedupe assertion

* fix(test): isolate github copilot token imports

* fix: keep active-descendant subagents visible in reply status

* refactor: dedupe helpers and source seams

* test: fix rebase gate regressions

* Adjust Feishu webhook request body limits (openclaw#53933)

* fix: dedupe stale subagent rows in reply views

* ci: batch shared extensions test lane

* fix: report deduped subagent totals

* fix: dedupe verbose subagent status counts

* fix: align /agents ids with subagent targets

* refactor: dedupe test helpers and harnesses

* perf(memory): builtin sqlite hot-path follow-ups (openclaw#53939)

* chore(perf): start builtin sqlite hotpath workstream

* perf(memory): reuse sqlite statements during sync

* perf(memory): snapshot file state during sync

* perf(memory): consolidate status sqlite reads

* docs(changelog): note builtin sqlite perf work

* perf(memory): avoid session table scans on targeted sync

* test: speed up memory provider suites

* test: speed up slack monitor suites

* test: speed up discord channel suites

* test: speed up telegram and whatsapp suites

* ci: increase test shard fanout

* fix: clean up matrix /agents binding labels

* fix: dedupe active child session counts

* fix: dedupe restarted descendant session counts

* fix: blcok non-owner authorized senders from chaning /send policy (openclaw#53994)

* fix(slack): trim DM reply overhead and restore Codex auto transport (openclaw#53957)

* perf(slack): instrument runtime and trim DM overhead

* perf(slack): lazy-init draft previews

* perf(slack): add turn summary diagnostics

* perf(core): trim repeated runtime setup noise

* perf(core): preselect default web search providers

* perf(agent): restore OpenAI auto transport defaults

* refactor(slack): drop temporary perf wiring

* fix(slack): address follow-up review notes

* fix(security): tighten slack and runtime defaults

* style(web-search): fix import ordering

* style(agent): remove useless spread fallback

* docs(changelog): note slack runtime hardening

* test: speed up discord monitor suites

* test: speed up cli and command suites

* test: speed up slack monitor suites

* fix: ignore stale rows in subagent activity checks

* fix: prefer latest subagent rows for session control

* fix: ignore stale rows in subagent admin kill

* fix: dedupe stale child completion announces

* fix: ignore stale rows in subagent steer

* fix: cascade bulk subagent kills past stale rows

* fix: address FootGun's PR #8 review — regenerate metadata + fix Zulip imports

1. Regenerated bundled-plugin-metadata.generated.ts (stale after upstream merge)
2. Fixed Zulip extension monolithic plugin-sdk imports:
   - OpenClawPluginApi → openclaw/plugin-sdk/plugin-entry
   - emptyPluginConfigSchema, PluginRuntime, OpenClawConfig → openclaw/plugin-sdk/core
   - ChannelAccountSnapshot inline imports → openclaw/plugin-sdk/zulip
3. Added ChannelAccountSnapshot re-export to src/plugin-sdk/zulip.ts

---------

Signed-off-by: HCL <[email protected]>
Signed-off-by: sallyom <[email protected]>
Co-authored-by: Devin Robison <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Co-authored-by: BunsDev <[email protected]>
Co-authored-by: Nova <[email protected]>
Co-authored-by: Rolfy <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: Taras Lukavyi <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: sudie-codes <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: giulio-leone <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: HCL <[email protected]>
Co-authored-by: Protocol-zero-0 <[email protected]>
Co-authored-by: Sid Uppal <[email protected]>
Co-authored-by: Catalin Lupuleti <[email protected]>
Co-authored-by: Tao Xie <[email protected]>
Co-authored-by: Tao Xie <[email protected]>
Co-authored-by: joelnishanth <[email protected]>
Co-authored-by: Mariano <[email protected]>
Co-authored-by: HollyChou <[email protected]>
Co-authored-by: altaywtf <[email protected]>
Co-authored-by: Neerav Makwana <[email protected]>
Co-authored-by: Sally O'Malley <[email protected]>
Co-authored-by: Harold Hunt <[email protected]>
Co-authored-by: huntharo <[email protected]>
Co-authored-by: David Guttman <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Onur Solmaz <[email protected]>
Co-authored-by: Han Pingtian <[email protected]>
Co-authored-by: Maho Pan <[email protected]>
Co-authored-by: Josh Lehman <[email protected]>
Co-authored-by: w-sss <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: Bob <[email protected]>
Co-authored-by: VioGarden <[email protected]>
Co-authored-by: scoootscooob <[email protected]>
Co-authored-by: Devin Robison <[email protected]>
netandreus pushed a commit to netandreus/openclaw that referenced this pull request Mar 25, 2026
npmisantosh pushed a commit to npmisantosh/openclaw that referenced this pull request Mar 25, 2026
godlin-gh pushed a commit to YouMindInc/openclaw that referenced this pull request Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes maintainer Maintainer-authored PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant