Skip to content
Open
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ jobs:
run: ${{ matrix.command }}

macos-app:
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request' && github.repository_owner == 'openclaw'
runs-on: macos-latest
strategy:
fail-fast: false
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Claude Code

on:
issues:
types: [opened, edited]
issue_comment:
types: [created, edited]

jobs:
claude:
runs-on: ubuntu-latest
# Only run when @claude is mentioned in issues/comments
if: contains(github.event.comment.body, '@claude') || contains(github.event.issue.body, '@claude')
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Run Claude Code
uses: anthropics/claude-code-action@v1
with:
prompt: |
${{ github.event.comment.body || github.event.issue.body }}
claude_args: |
--max-turns 10
env:
# Use OAuth token for Claude Max/Pro subscription (no API key needed)
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 2 additions & 0 deletions .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ permissions: {}

jobs:
label:
if: github.repository_owner == 'openclaw' && secrets.GH_APP_PRIVATE_KEY != ''
Copy link

Choose a reason for hiding this comment

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

secrets.GH_APP_PRIVATE_KEY reference won't work in if condition

Suggested change
if: github.repository_owner == 'openclaw' && secrets.GH_APP_PRIVATE_KEY != ''
if: github.repository_owner == 'openclaw'
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/labeler.yml
Line: 13:13

Comment:
`secrets.GH_APP_PRIVATE_KEY` reference won't work in `if` condition

```suggestion
    if: github.repository_owner == 'openclaw'
```

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

Copy link

Choose a reason for hiding this comment

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

Condition won't work as intended. Secrets aren't available in if conditions — they're always empty strings in workflow expressions. This check will always evaluate to false on forks.

Suggested change
if: github.repository_owner == 'openclaw' && secrets.GH_APP_PRIVATE_KEY != ''
if: github.repository_owner == 'openclaw'

The secret availability will be enforced when the step tries to use it.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/labeler.yml
Line: 13

Comment:
Condition won't work as intended. Secrets aren't available in `if` conditions — they're always empty strings in workflow expressions. This check will always evaluate to false on forks.

```suggestion
    if: github.repository_owner == 'openclaw'
```

The secret availability will be enforced when the step tries to use it.

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

permissions:
contents: read
pull-requests: write
Expand Down Expand Up @@ -48,6 +49,7 @@ jobs:
});

label-issues:
if: github.repository_owner == 'openclaw' && secrets.GH_APP_PRIVATE_KEY != ''
Copy link

Choose a reason for hiding this comment

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

Same issue — secret check in if won't work.

Suggested change
if: github.repository_owner == 'openclaw' && secrets.GH_APP_PRIVATE_KEY != ''
if: github.repository_owner == 'openclaw'
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/labeler.yml
Line: 52

Comment:
Same issue — secret check in `if` won't work.

```suggestion
    if: github.repository_owner == 'openclaw'
```

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

permissions:
issues: write
runs-on: ubuntu-latest
Expand Down
21 changes: 18 additions & 3 deletions extensions/memory-lancedb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* Provides seamless auto-recall and auto-capture via lifecycle hooks.
*/

import type * as LanceDB from "@lancedb/lancedb";
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import * as lancedb from "@lancedb/lancedb";
import { Type } from "@sinclair/typebox";
import { randomUUID } from "node:crypto";
import OpenAI from "openai";
Expand Down Expand Up @@ -44,8 +44,9 @@ type MemorySearchResult = {
const TABLE_NAME = "memories";

class MemoryDB {
private db: lancedb.Connection | null = null;
private table: lancedb.Table | null = null;
private lancedb: typeof import("@lancedb/lancedb") | null = null;
private db: LanceDB.Connection | null = null;
private table: LanceDB.Table | null = null;
private initPromise: Promise<void> | null = null;

constructor(
Expand All @@ -65,7 +66,21 @@ class MemoryDB {
return this.initPromise;
}

private async getLanceDB(): Promise<typeof import("@lancedb/lancedb")> {
if (this.lancedb) return this.lancedb;
try {
this.lancedb = await import("@lancedb/lancedb");
return this.lancedb;
} catch (err) {
// LanceDB currently ships platform-specific native deps. Fail gracefully on unsupported platforms.
throw new Error(
`memory-lancedb: LanceDB native module unavailable on ${process.platform}/${process.arch}: ${String(err)}`,
);
}
}

private async doInitialize(): Promise<void> {
const lancedb = await this.getLanceDB();
this.db = await lancedb.connect(this.dbPath);
const tables = await this.db.tableNames();

Expand Down
1 change: 1 addition & 0 deletions hooks/logs/hooks.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"timestamp":"2026-02-07T03:11:12.846Z","level":"debug","hook":"voice-macos","event":"voice","message":"Speech completed"}
6 changes: 6 additions & 0 deletions src/channels/plugins/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export type ChannelUiMetaEntry = {
label: string;
detailLabel: string;
systemImage?: string;
description?: string;
docsPath?: string;
docsLabel?: string;
};

export type ChannelUiCatalog = {
Expand Down Expand Up @@ -242,6 +245,9 @@ export function buildChannelUiCatalog(
label: plugin.meta.label,
detailLabel,
...(plugin.meta.systemImage ? { systemImage: plugin.meta.systemImage } : {}),
...(plugin.meta.blurb ? { description: plugin.meta.blurb } : {}),
...(plugin.meta.docsPath ? { docsPath: plugin.meta.docsPath } : {}),
...(plugin.meta.docsLabel ? { docsLabel: plugin.meta.docsLabel } : {}),
};
});
const order = entries.map((entry) => entry.id);
Expand Down
22 changes: 22 additions & 0 deletions src/channels/plugins/types.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,32 @@ import type {
export type ChannelConfigUiHint = {
label?: string;
help?: string;
group?: string;
order?: number;
advanced?: boolean;
sensitive?: boolean;
placeholder?: string;
itemTemplate?: unknown;
docsPath?: string;
impacts?: Array<{
relation?: "requires" | "conflicts" | "recommends" | "risk";
targetPath?: string;
when?: "truthy" | "falsy" | "defined" | "notDefined" | "equals" | "notEquals" | "includes";
whenValue?: unknown;
targetWhen?:
| "truthy"
| "falsy"
| "defined"
| "notDefined"
| "equals"
| "notEquals"
| "includes";
targetValue?: unknown;
message: string;
fixValue?: unknown;
fixLabel?: string;
docsPath?: string;
}>;
};

export type ChannelConfigSchema = {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/gateway-cli/run-loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function runGatewayLoop(params: {
gatewayLog.error("shutdown timed out; exiting without full cleanup");
cleanupSignals();
params.runtime.exit(0);
}, 5000);
}, 15_000);

void (async () => {
try {
Expand Down
26 changes: 26 additions & 0 deletions src/config/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,30 @@ describe("config schema", () => {
expect(defaultsHint?.help).toContain("last");
expect(listHint?.help).toContain("bluebubbles");
});

it("adds docs and impact metadata for high-risk settings", () => {
const res = buildConfigSchema({
channels: [
{
id: "discord",
label: "Discord",
description: "Discord bot channel",
docsPath: "/channels/discord",
configSchema: { type: "object" },
},
],
});

expect(res.uiHints["gateway.mode"]?.docsPath).toBe("/web/dashboard");
expect(res.uiHints["gateway.mode"]?.impacts?.length).toBeGreaterThan(0);
expect(res.uiHints["channels.discord"]?.docsPath).toBe("/channels/discord");
expect(res.uiHints["channels.discord.actions.roles"]?.docsPath).toBe("/channels/discord");
expect(res.uiHints["channels.discord.actions.roles"]?.impacts?.[0]?.relation).toBe("requires");
expect(res.uiHints["channels.*.allowBots"]?.docsPath).toBe("/gateway/configuration");
expect(res.uiHints["channels.*.allowBots"]?.impacts?.[0]?.relation).toBe("risk");
expect(res.uiHints["channels.*.dm.policy"]?.impacts?.[0]?.targetPath).toBe(
"channels.*.dm.allowFrom",
);
expect(res.uiHints["channels.telegram.streamMode"]?.impacts?.[0]?.relation).toBe("conflicts");
});
});
Loading
Loading