Merge branch 'main' into perplexity-search

This commit is contained in:
Kesku 2026-01-25 14:41:39 -08:00 committed by GitHub
commit 37f1380742
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 291 additions and 56 deletions

View File

@ -2,11 +2,18 @@
Docs: https://docs.clawd.bot
## 2026.1.25
Status: unreleased.
### Changes
- TBD.
## 2026.1.24-3
### Fixes
- Gateway: harden reverse proxy handling for local-client detection and unauthenticated proxied connects. (#1795) Thanks @orlyjamie.
- Security audit: flag loopback Control UI with auth disabled as critical. (#1795) Thanks @orlyjamie.
- CLI: resume claude-cli sessions and stream CLI replies to TUI clients. (#1921) Thanks @rmorse.
## 2026.1.24-2

View File

@ -21,8 +21,8 @@ android {
applicationId = "com.clawdbot.android"
minSdk = 31
targetSdk = 36
versionCode = 202601240
versionName = "2026.1.24"
versionCode = 202601250
versionName = "2026.1.25"
}
buildTypes {

View File

@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.1.24</string>
<string>2026.1.25</string>
<key>CFBundleVersion</key>
<string>20260124</string>
<string>20260125</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoadsInWebContent</key>

View File

@ -17,8 +17,8 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>2026.1.24</string>
<string>2026.1.25</string>
<key>CFBundleVersion</key>
<string>20260124</string>
<string>20260125</string>
</dict>
</plist>

View File

@ -81,8 +81,8 @@ targets:
properties:
CFBundleDisplayName: Clawdbot
CFBundleIconName: AppIcon
CFBundleShortVersionString: "2026.1.24"
CFBundleVersion: "20260124"
CFBundleShortVersionString: "2026.1.25"
CFBundleVersion: "20260125"
UILaunchScreen: {}
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: false
@ -130,5 +130,5 @@ targets:
path: Tests/Info.plist
properties:
CFBundleDisplayName: ClawdbotTests
CFBundleShortVersionString: "2026.1.24"
CFBundleVersion: "20260124"
CFBundleShortVersionString: "2026.1.25"
CFBundleVersion: "20260125"

View File

@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.1.24</string>
<string>2026.1.25</string>
<key>CFBundleVersion</key>
<string>202601240</string>
<string>202601250</string>
<key>CFBundleIconFile</key>
<string>Clawdbot</string>
<key>CFBundleURLTypes</key>

View File

@ -182,6 +182,7 @@ Clawdbot ships a default for `claude-cli`:
- `command: "claude"`
- `args: ["-p", "--output-format", "json", "--dangerously-skip-permissions"]`
- `resumeArgs: ["-p", "--output-format", "json", "--dangerously-skip-permissions", "--resume", "{sessionId}"]`
- `modelArg: "--model"`
- `systemPromptArg: "--append-system-prompt"`
- `sessionArg: "--session-id"`

View File

@ -182,7 +182,7 @@ cat > /data/clawdbot.json << 'EOF'
"bind": "auto"
},
"meta": {
"lastTouchedVersion": "2026.1.24"
"lastTouchedVersion": "2026.1.25"
}
}
EOF

View File

@ -30,17 +30,17 @@ Notes:
# From repo root; set release IDs so Sparkle feed is enabled.
# APP_BUILD must be numeric + monotonic for Sparkle compare.
BUNDLE_ID=com.clawdbot.mac \
APP_VERSION=2026.1.24-3 \
APP_VERSION=2026.1.25 \
APP_BUILD="$(git rev-list --count HEAD)" \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
scripts/package-mac-app.sh
# Zip for distribution (includes resource forks for Sparkle delta support)
ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.24-3.zip
ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.25.zip
# Optional: also build a styled DMG for humans (drag to /Applications)
scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24-3.dmg
scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.25.dmg
# Recommended: build + notarize/staple zip + DMG
# First, create a keychain profile once:
@ -48,26 +48,26 @@ scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24-3.dmg
# --apple-id "<apple-id>" --team-id "<team-id>" --password "<app-specific-password>"
NOTARIZE=1 NOTARYTOOL_PROFILE=clawdbot-notary \
BUNDLE_ID=com.clawdbot.mac \
APP_VERSION=2026.1.24-3 \
APP_VERSION=2026.1.25 \
APP_BUILD="$(git rev-list --count HEAD)" \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: <Developer Name> (<TEAMID>)" \
scripts/package-mac-dist.sh
# Optional: ship dSYM alongside the release
ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.24-3.dSYM.zip
ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.25.dSYM.zip
```
## Appcast entry
Use the release note generator so Sparkle renders formatted HTML notes:
```bash
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Clawdbot-2026.1.24-3.zip https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Clawdbot-2026.1.25.zip https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
```
Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/clawdbot/clawdbot/blob/main/scripts/changelog-to-html.sh)) and embeds them in the appcast entry.
Commit the updated `appcast.xml` alongside the release assets (zip + dSYM) when publishing.
## Publish & verify
- Upload `Clawdbot-2026.1.24-3.zip` (and `Clawdbot-2026.1.24-3.dSYM.zip`) to the GitHub release for tag `v2026.1.24-3`.
- Upload `Clawdbot-2026.1.25.zip` (and `Clawdbot-2026.1.25.dSYM.zip`) to the GitHub release for tag `v2026.1.25`.
- Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml`.
- Sanity checks:
- `curl -I https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml` returns 200.

View File

@ -16,7 +16,7 @@ and you configure everything via the `/setup` web wizard.
## One-click deploy
<a href="https://railway.app/new/template?template=https://github.com/vignesh07/clawdbot-railway-template" target="_blank" rel="noreferrer">Deploy on Railway</a>
<a href="https://railway.com/deploy/clawdbot-railway-template" target="_blank" rel="noreferrer">Deploy on Railway</a>
After deploy, find your public URL in **Railway → your service → Settings → Domains**.

View File

@ -17,7 +17,7 @@ When the operator says “release”, immediately do this preflight (no extra qu
- Use Sparkle keys from `~/Library/CloudStorage/Dropbox/Backup/Sparkle` if needed.
1) **Version & metadata**
- [ ] Bump `package.json` version (e.g., `2026.1.24`).
- [ ] Bump `package.json` version (e.g., `2026.1.25`).
- [ ] Run `pnpm plugins:sync` to align extension package versions + changelogs.
- [ ] Update CLI/version strings: [`src/cli/program.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/cli/program.ts) and the Baileys user agent in [`src/provider-web.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/provider-web.ts).
- [ ] Confirm package metadata (name, description, repository, keywords, license) and `bin` map points to [`dist/entry.js`](https://github.com/clawdbot/clawdbot/blob/main/dist/entry.js) for `clawdbot`.

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/bluebubbles",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot BlueBubbles channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/copilot-proxy",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Copilot Proxy provider plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/diagnostics-otel",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot diagnostics OpenTelemetry exporter",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/discord",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Discord channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/google-antigravity-auth",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Google Antigravity OAuth provider plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/google-gemini-cli-auth",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Gemini CLI OAuth provider plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/googlechat",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Google Chat channel plugin",
"clawdbot": {
@ -34,6 +34,6 @@
"clawdbot": "workspace:*"
},
"peerDependencies": {
"clawdbot": ">=2026.1.24"
"clawdbot": ">=2026.1.25"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/imessage",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot iMessage channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/line",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot LINE channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/llm-task",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot JSON-only LLM task plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/lobster",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/matrix",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Matrix channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/mattermost",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Mattermost channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/memory-core",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot core memory search plugin",
"clawdbot": {
@ -9,6 +9,6 @@
]
},
"peerDependencies": {
"clawdbot": ">=2026.1.24"
"clawdbot": ">=2026.1.25"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/memory-lancedb",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot LanceDB-backed long-term memory plugin with auto-recall/capture",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/msteams",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Microsoft Teams channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/nextcloud-talk",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Nextcloud Talk channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/nostr",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Nostr channel plugin for NIP-04 encrypted DMs",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/open-prose",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "OpenProse VM skill pack plugin (slash command + telemetry).",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/signal",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Signal channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/slack",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Slack channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/telegram",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Telegram channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/tlon",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Tlon/Urbit channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
# Changelog
## 2026.1.24
## 2026.1.25
### Changes
- Breaking: voice-call TTS now uses core `messages.tts` (plugin TTS config deepmerges with core).

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/voice-call",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot voice-call plugin",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/whatsapp",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot WhatsApp channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/zalo",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Zalo channel plugin",
"clawdbot": {

View File

@ -1,6 +1,6 @@
{
"name": "@clawdbot/zalouser",
"version": "2026.1.24",
"version": "2026.1.25",
"type": "module",
"description": "Clawdbot Zalo Personal Account plugin via zca-cli",
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "clawdbot",
"version": "2026.1.24-3",
"version": "2026.1.25",
"description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent",
"type": "module",
"main": "dist/index.js",
@ -220,7 +220,7 @@
"@types/proper-lockfile": "^4.1.4",
"@types/qrcode-terminal": "^0.12.2",
"@types/ws": "^8.18.1",
"@typescript/native-preview": "7.0.0-dev.20260124.1",
"@typescript/native-preview": "7.0.0-dev.20260125.1",
"@vitest/coverage-v8": "^4.0.18",
"docx-preview": "^0.3.7",
"lit": "^3.3.2",

View File

@ -61,7 +61,7 @@ describe("runClaudeCliAgent", () => {
expect(argv).toContain("hi");
});
it("uses provided --session-id when a claude session id is provided", async () => {
it("uses --resume when a claude session id is provided", async () => {
runCommandWithTimeoutMock.mockResolvedValueOnce({
stdout: JSON.stringify({ message: "ok", session_id: "sid-2" }),
stderr: "",
@ -83,7 +83,7 @@ describe("runClaudeCliAgent", () => {
expect(runCommandWithTimeoutMock).toHaveBeenCalledTimes(1);
const argv = runCommandWithTimeoutMock.mock.calls[0]?.[0] as string[];
expect(argv).toContain("--session-id");
expect(argv).toContain("--resume");
expect(argv).toContain("c9d7b831-1c31-4d22-80b9-1e50ca207d4b");
expect(argv).toContain("hi");
});

View File

@ -28,6 +28,14 @@ const CLAUDE_MODEL_ALIASES: Record<string, string> = {
const DEFAULT_CLAUDE_BACKEND: CliBackendConfig = {
command: "claude",
args: ["-p", "--output-format", "json", "--dangerously-skip-permissions"],
resumeArgs: [
"-p",
"--output-format",
"json",
"--dangerously-skip-permissions",
"--resume",
"{sessionId}",
],
output: "json",
input: "arg",
modelArg: "--model",

View File

@ -179,6 +179,17 @@ export async function runAgentTurnWithFallback(params: {
images: params.opts?.images,
})
.then((result) => {
// CLI backends don't emit streaming assistant events, so we need to
// emit one with the final text so server-chat can populate its buffer
// and send the response to TUI/WebSocket clients.
const cliText = result.payloads?.[0]?.text?.trim();
if (cliText) {
emitAgentEvent({
runId,
stream: "assistant",
data: { text: cliText },
});
}
emitAgentEvent({
runId,
stream: "lifecycle",

View File

@ -0,0 +1,43 @@
import { describe, expect, it, vi } from "vitest";
import { createAgentEventHandler, createChatRunState } from "./server-chat.js";
describe("agent event handler", () => {
it("emits chat delta for assistant text-only events", () => {
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_000);
const broadcast = vi.fn();
const nodeSendToSession = vi.fn();
const agentRunSeq = new Map<string, number>();
const chatRunState = createChatRunState();
chatRunState.registry.add("run-1", { sessionKey: "session-1", clientRunId: "client-1" });
const handler = createAgentEventHandler({
broadcast,
nodeSendToSession,
agentRunSeq,
chatRunState,
resolveSessionKeyForRun: () => undefined,
clearAgentRunContext: vi.fn(),
});
handler({
runId: "run-1",
seq: 1,
stream: "assistant",
ts: Date.now(),
data: { text: "Hello world" },
});
const chatCalls = broadcast.mock.calls.filter(([event]) => event === "chat");
expect(chatCalls).toHaveLength(1);
const payload = chatCalls[0]?.[1] as {
state?: string;
message?: { content?: Array<{ text?: string }> };
};
expect(payload.state).toBe("delta");
expect(payload.message?.content?.[0]?.text).toBe("Hello world");
const sessionChatCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "chat");
expect(sessionChatCalls).toHaveLength(1);
nowSpy.mockRestore();
});
});

View File

@ -0,0 +1,163 @@
import { describe, expect, it, vi } from "vitest";
import type { GatewayRequestContext } from "./types.js";
import { agentHandlers } from "./agent.js";
const mocks = vi.hoisted(() => ({
loadSessionEntry: vi.fn(),
updateSessionStore: vi.fn(),
agentCommand: vi.fn(),
registerAgentRunContext: vi.fn(),
}));
vi.mock("../session-utils.js", () => ({
loadSessionEntry: mocks.loadSessionEntry,
}));
vi.mock("../../config/sessions.js", async () => {
const actual = await vi.importActual<typeof import("../../config/sessions.js")>(
"../../config/sessions.js",
);
return {
...actual,
updateSessionStore: mocks.updateSessionStore,
resolveAgentIdFromSessionKey: () => "main",
resolveExplicitAgentSessionKey: () => undefined,
resolveAgentMainSessionKey: () => "agent:main:main",
};
});
vi.mock("../../commands/agent.js", () => ({
agentCommand: mocks.agentCommand,
}));
vi.mock("../../config/config.js", () => ({
loadConfig: () => ({}),
}));
vi.mock("../../agents/agent-scope.js", () => ({
listAgentIds: () => ["main"],
}));
vi.mock("../../infra/agent-events.js", () => ({
registerAgentRunContext: mocks.registerAgentRunContext,
onAgentEvent: vi.fn(),
}));
vi.mock("../../sessions/send-policy.js", () => ({
resolveSendPolicy: () => "allow",
}));
vi.mock("../../utils/delivery-context.js", async () => {
const actual = await vi.importActual<typeof import("../../utils/delivery-context.js")>(
"../../utils/delivery-context.js",
);
return {
...actual,
normalizeSessionDeliveryFields: () => ({}),
};
});
const makeContext = (): GatewayRequestContext =>
({
dedupe: new Map(),
addChatRun: vi.fn(),
logGateway: { info: vi.fn(), error: vi.fn() },
}) as unknown as GatewayRequestContext;
describe("gateway agent handler", () => {
it("preserves cliSessionIds from existing session entry", async () => {
const existingCliSessionIds = { "claude-cli": "abc-123-def" };
const existingClaudeCliSessionId = "abc-123-def";
mocks.loadSessionEntry.mockReturnValue({
cfg: {},
storePath: "/tmp/sessions.json",
entry: {
sessionId: "existing-session-id",
updatedAt: Date.now(),
cliSessionIds: existingCliSessionIds,
claudeCliSessionId: existingClaudeCliSessionId,
},
canonicalKey: "agent:main:main",
});
let capturedEntry: Record<string, unknown> | undefined;
mocks.updateSessionStore.mockImplementation(async (_path, updater) => {
const store: Record<string, unknown> = {};
await updater(store);
capturedEntry = store["agent:main:main"] as Record<string, unknown>;
});
mocks.agentCommand.mockResolvedValue({
payloads: [{ text: "ok" }],
meta: { durationMs: 100 },
});
const respond = vi.fn();
await agentHandlers.agent({
params: {
message: "test",
agentId: "main",
sessionKey: "agent:main:main",
idempotencyKey: "test-idem",
},
respond,
context: makeContext(),
req: { type: "req", id: "1", method: "agent" },
client: null,
isWebchatConnect: () => false,
});
expect(mocks.updateSessionStore).toHaveBeenCalled();
expect(capturedEntry).toBeDefined();
expect(capturedEntry?.cliSessionIds).toEqual(existingCliSessionIds);
expect(capturedEntry?.claudeCliSessionId).toBe(existingClaudeCliSessionId);
});
it("handles missing cliSessionIds gracefully", async () => {
mocks.loadSessionEntry.mockReturnValue({
cfg: {},
storePath: "/tmp/sessions.json",
entry: {
sessionId: "existing-session-id",
updatedAt: Date.now(),
// No cliSessionIds or claudeCliSessionId
},
canonicalKey: "agent:main:main",
});
let capturedEntry: Record<string, unknown> | undefined;
mocks.updateSessionStore.mockImplementation(async (_path, updater) => {
const store: Record<string, unknown> = {};
await updater(store);
capturedEntry = store["agent:main:main"] as Record<string, unknown>;
});
mocks.agentCommand.mockResolvedValue({
payloads: [{ text: "ok" }],
meta: { durationMs: 100 },
});
const respond = vi.fn();
await agentHandlers.agent({
params: {
message: "test",
agentId: "main",
sessionKey: "agent:main:main",
idempotencyKey: "test-idem-2",
},
respond,
context: makeContext(),
req: { type: "req", id: "2", method: "agent" },
client: null,
isWebchatConnect: () => false,
});
expect(mocks.updateSessionStore).toHaveBeenCalled();
expect(capturedEntry).toBeDefined();
// Should be undefined, not cause an error
expect(capturedEntry?.cliSessionIds).toBeUndefined();
expect(capturedEntry?.claudeCliSessionId).toBeUndefined();
});
});

View File

@ -251,6 +251,8 @@ export const agentHandlers: GatewayRequestHandlers = {
groupId: resolvedGroupId ?? entry?.groupId,
groupChannel: resolvedGroupChannel ?? entry?.groupChannel,
space: resolvedGroupSpace ?? entry?.space,
cliSessionIds: entry?.cliSessionIds,
claudeCliSessionId: entry?.claudeCliSessionId,
};
sessionEntry = nextEntry;
const sendPolicy = resolveSendPolicy({