refactor: require session state for directive handling
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Docs: https://docs.clawd.bot
|
Docs: https://docs.clawd.bot
|
||||||
|
|
||||||
## 2026.1.22
|
## 2026.1.22 (unreleased)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell.
|
- BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell.
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export async function applyInlineDirectivesFastLane(params: {
|
|||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
agentId?: string;
|
agentId?: string;
|
||||||
isGroup: boolean;
|
isGroup: boolean;
|
||||||
sessionEntry?: SessionEntry;
|
sessionEntry: SessionEntry;
|
||||||
sessionStore?: Record<string, SessionEntry>;
|
sessionStore: Record<string, SessionEntry>;
|
||||||
sessionKey: string;
|
sessionKey: string;
|
||||||
storePath?: string;
|
storePath?: string;
|
||||||
elevatedEnabled: boolean;
|
elevatedEnabled: boolean;
|
||||||
|
|||||||
@@ -77,100 +77,6 @@ describe("handleDirectiveOnly model persist behavior (fixes #1435)", () => {
|
|||||||
expect(result?.text).not.toContain("failed");
|
expect(result?.text).not.toContain("failed");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows error message when sessionEntry is missing", async () => {
|
|
||||||
const directives = parseInlineDirectives("/model openai/gpt-4o");
|
|
||||||
const sessionStore = {};
|
|
||||||
|
|
||||||
const result = await handleDirectiveOnly({
|
|
||||||
cfg: baseConfig(),
|
|
||||||
directives,
|
|
||||||
sessionEntry: undefined, // Missing!
|
|
||||||
sessionStore,
|
|
||||||
sessionKey: "agent:main:dm:1",
|
|
||||||
storePath: "/tmp/sessions.json",
|
|
||||||
elevatedEnabled: false,
|
|
||||||
elevatedAllowed: false,
|
|
||||||
defaultProvider: "anthropic",
|
|
||||||
defaultModel: "claude-opus-4-5",
|
|
||||||
aliasIndex: baseAliasIndex(),
|
|
||||||
allowedModelKeys,
|
|
||||||
allowedModelCatalog,
|
|
||||||
resetModelOverride: false,
|
|
||||||
provider: "anthropic",
|
|
||||||
model: "claude-opus-4-5",
|
|
||||||
initialModelLabel: "anthropic/claude-opus-4-5",
|
|
||||||
formatModelSwitchEvent: (label) => `Switched to ${label}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result?.text).toContain("failed");
|
|
||||||
expect(result?.text).toContain("session state unavailable");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows error message when sessionStore is missing", async () => {
|
|
||||||
const directives = parseInlineDirectives("/model openai/gpt-4o");
|
|
||||||
const sessionEntry: SessionEntry = {
|
|
||||||
sessionId: "s1",
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await handleDirectiveOnly({
|
|
||||||
cfg: baseConfig(),
|
|
||||||
directives,
|
|
||||||
sessionEntry,
|
|
||||||
sessionStore: undefined, // Missing!
|
|
||||||
sessionKey: "agent:main:dm:1",
|
|
||||||
storePath: "/tmp/sessions.json",
|
|
||||||
elevatedEnabled: false,
|
|
||||||
elevatedAllowed: false,
|
|
||||||
defaultProvider: "anthropic",
|
|
||||||
defaultModel: "claude-opus-4-5",
|
|
||||||
aliasIndex: baseAliasIndex(),
|
|
||||||
allowedModelKeys,
|
|
||||||
allowedModelCatalog,
|
|
||||||
resetModelOverride: false,
|
|
||||||
provider: "anthropic",
|
|
||||||
model: "claude-opus-4-5",
|
|
||||||
initialModelLabel: "anthropic/claude-opus-4-5",
|
|
||||||
formatModelSwitchEvent: (label) => `Switched to ${label}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result?.text).toContain("failed");
|
|
||||||
expect(result?.text).toContain("session state unavailable");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows error message when sessionKey is missing", async () => {
|
|
||||||
const directives = parseInlineDirectives("/model openai/gpt-4o");
|
|
||||||
const sessionEntry: SessionEntry = {
|
|
||||||
sessionId: "s1",
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
};
|
|
||||||
const sessionStore = { "agent:main:dm:1": sessionEntry };
|
|
||||||
|
|
||||||
const result = await handleDirectiveOnly({
|
|
||||||
cfg: baseConfig(),
|
|
||||||
directives,
|
|
||||||
sessionEntry,
|
|
||||||
sessionStore,
|
|
||||||
sessionKey: undefined, // Missing!
|
|
||||||
storePath: "/tmp/sessions.json",
|
|
||||||
elevatedEnabled: false,
|
|
||||||
elevatedAllowed: false,
|
|
||||||
defaultProvider: "anthropic",
|
|
||||||
defaultModel: "claude-opus-4-5",
|
|
||||||
aliasIndex: baseAliasIndex(),
|
|
||||||
allowedModelKeys,
|
|
||||||
allowedModelCatalog,
|
|
||||||
resetModelOverride: false,
|
|
||||||
provider: "anthropic",
|
|
||||||
model: "claude-opus-4-5",
|
|
||||||
initialModelLabel: "anthropic/claude-opus-4-5",
|
|
||||||
formatModelSwitchEvent: (label) => `Switched to ${label}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result?.text).toContain("failed");
|
|
||||||
expect(result?.text).toContain("session state unavailable");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("shows no model message when no /model directive", async () => {
|
it("shows no model message when no /model directive", async () => {
|
||||||
const directives = parseInlineDirectives("hello world");
|
const directives = parseInlineDirectives("hello world");
|
||||||
const sessionEntry: SessionEntry = {
|
const sessionEntry: SessionEntry = {
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ function resolveExecDefaults(params: {
|
|||||||
export async function handleDirectiveOnly(params: {
|
export async function handleDirectiveOnly(params: {
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
directives: InlineDirectives;
|
directives: InlineDirectives;
|
||||||
sessionEntry?: SessionEntry;
|
sessionEntry: SessionEntry;
|
||||||
sessionStore?: Record<string, SessionEntry>;
|
sessionStore: Record<string, SessionEntry>;
|
||||||
sessionKey: string;
|
sessionKey: string;
|
||||||
storePath?: string;
|
storePath?: string;
|
||||||
elevatedEnabled: boolean;
|
elevatedEnabled: boolean;
|
||||||
@@ -288,8 +288,6 @@ export async function handleDirectiveOnly(params: {
|
|||||||
nextThinkLevel === "xhigh" &&
|
nextThinkLevel === "xhigh" &&
|
||||||
!supportsXHighThinking(resolvedProvider, resolvedModel);
|
!supportsXHighThinking(resolvedProvider, resolvedModel);
|
||||||
|
|
||||||
let didPersistModel = false;
|
|
||||||
if (sessionEntry && sessionStore && sessionKey) {
|
|
||||||
const prevElevatedLevel =
|
const prevElevatedLevel =
|
||||||
currentElevatedLevel ??
|
currentElevatedLevel ??
|
||||||
(sessionEntry.elevatedLevel as ElevatedLevel | undefined) ??
|
(sessionEntry.elevatedLevel as ElevatedLevel | undefined) ??
|
||||||
@@ -347,7 +345,6 @@ export async function handleDirectiveOnly(params: {
|
|||||||
selection: modelSelection,
|
selection: modelSelection,
|
||||||
profileOverride,
|
profileOverride,
|
||||||
});
|
});
|
||||||
didPersistModel = true;
|
|
||||||
}
|
}
|
||||||
if (directives.hasQueueDirective && directives.queueReset) {
|
if (directives.hasQueueDirective && directives.queueReset) {
|
||||||
delete sessionEntry.queueMode;
|
delete sessionEntry.queueMode;
|
||||||
@@ -396,7 +393,6 @@ export async function handleDirectiveOnly(params: {
|
|||||||
contextKey: "mode:reasoning",
|
contextKey: "mode:reasoning",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
if (directives.hasThinkDirective && directives.thinkLevel) {
|
if (directives.hasThinkDirective && directives.thinkLevel) {
|
||||||
@@ -449,7 +445,7 @@ export async function handleDirectiveOnly(params: {
|
|||||||
`Thinking level set to high (xhigh not supported for ${resolvedProvider}/${resolvedModel}).`,
|
`Thinking level set to high (xhigh not supported for ${resolvedProvider}/${resolvedModel}).`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (modelSelection && didPersistModel) {
|
if (modelSelection) {
|
||||||
const label = `${modelSelection.provider}/${modelSelection.model}`;
|
const label = `${modelSelection.provider}/${modelSelection.model}`;
|
||||||
const labelWithAlias = modelSelection.alias ? `${modelSelection.alias} (${label})` : label;
|
const labelWithAlias = modelSelection.alias ? `${modelSelection.alias} (${label})` : label;
|
||||||
parts.push(
|
parts.push(
|
||||||
@@ -460,10 +456,6 @@ export async function handleDirectiveOnly(params: {
|
|||||||
if (profileOverride) {
|
if (profileOverride) {
|
||||||
parts.push(`Auth profile set to ${profileOverride}.`);
|
parts.push(`Auth profile set to ${profileOverride}.`);
|
||||||
}
|
}
|
||||||
} else if (modelSelection && !didPersistModel) {
|
|
||||||
parts.push(
|
|
||||||
`Model switch to ${modelSelection.provider}/${modelSelection.model} failed (session state unavailable).`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (directives.hasQueueDirective && directives.queueMode) {
|
if (directives.hasQueueDirective && directives.queueMode) {
|
||||||
parts.push(formatDirectiveAck(`Queue mode set to ${directives.queueMode}.`));
|
parts.push(formatDirectiveAck(`Queue mode set to ${directives.queueMode}.`));
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ export async function applyInlineDirectiveOverrides(params: {
|
|||||||
agentId: string;
|
agentId: string;
|
||||||
agentDir: string;
|
agentDir: string;
|
||||||
agentCfg: AgentDefaults;
|
agentCfg: AgentDefaults;
|
||||||
sessionEntry?: SessionEntry;
|
sessionEntry: SessionEntry;
|
||||||
sessionStore?: Record<string, SessionEntry>;
|
sessionStore: Record<string, SessionEntry>;
|
||||||
sessionKey: string;
|
sessionKey: string;
|
||||||
storePath?: string;
|
storePath?: string;
|
||||||
sessionScope: Parameters<typeof buildStatusReply>[0]["sessionScope"];
|
sessionScope: Parameters<typeof buildStatusReply>[0]["sessionScope"];
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ export async function resolveReplyDirectives(params: {
|
|||||||
workspaceDir: string;
|
workspaceDir: string;
|
||||||
agentCfg: AgentDefaults;
|
agentCfg: AgentDefaults;
|
||||||
sessionCtx: TemplateContext;
|
sessionCtx: TemplateContext;
|
||||||
sessionEntry?: SessionEntry;
|
sessionEntry: SessionEntry;
|
||||||
sessionStore?: Record<string, SessionEntry>;
|
sessionStore: Record<string, SessionEntry>;
|
||||||
sessionKey: string;
|
sessionKey: string;
|
||||||
storePath?: string;
|
storePath?: string;
|
||||||
sessionScope: Parameters<typeof applyInlineDirectiveOverrides>[0]["sessionScope"];
|
sessionScope: Parameters<typeof applyInlineDirectiveOverrides>[0]["sessionScope"];
|
||||||
|
|||||||
Reference in New Issue
Block a user