mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:30:57 +00:00
fix: preserve terminal session lifecycle state
This commit is contained in:
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
|
- Agents/sessions: preserve terminal lifecycle state when final run metadata persists from a stale in-memory snapshot, preventing `main` sessions from staying stuck as running after completed or timed-out turns.
|
||||||
- Status: show the `openai-codex` OAuth profile for `openai/gpt-*` sessions running through the native Codex runtime instead of reporting auth as unknown. (#76197) Thanks @mbelinky.
|
- Status: show the `openai-codex` OAuth profile for `openai/gpt-*` sessions running through the native Codex runtime instead of reporting auth as unknown. (#76197) Thanks @mbelinky.
|
||||||
- Plugins/externalization: keep diagnostics ClawHub packages and persisted bundled-plugin relocation on npm-first install metadata for launch, and omit Discord from the core package now that its external package is published. Thanks @vincentkoc.
|
- Plugins/externalization: keep diagnostics ClawHub packages and persisted bundled-plugin relocation on npm-first install metadata for launch, and omit Discord from the core package now that its external package is published. Thanks @vincentkoc.
|
||||||
- Plugins/Codex: allow the official npm Codex plugin to install without the unsafe-install override, keep `/codex` command ownership, and cover the real npm Docker live path through managed `.openclaw/npm` dependencies plus uninstall failure proof.
|
- Plugins/Codex: allow the official npm Codex plugin to install without the unsafe-install override, keep `/codex` command ownership, and cover the real npm Docker live path through managed `.openclaw/npm` dependencies plus uninstall failure proof.
|
||||||
|
|||||||
@@ -388,6 +388,63 @@ describe("updateSessionStoreAfterAgentRun", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves terminal lifecycle state when caller has a stale running snapshot", async () => {
|
||||||
|
await withTempSessionStore(async ({ storePath }) => {
|
||||||
|
const cfg = {} as OpenClawConfig;
|
||||||
|
const sessionKey = "agent:main:explicit:test-lifecycle-preserve";
|
||||||
|
const sessionId = "test-lifecycle-preserve-session";
|
||||||
|
const terminalEntry: SessionEntry = {
|
||||||
|
sessionId,
|
||||||
|
updatedAt: 2_000,
|
||||||
|
status: "done",
|
||||||
|
startedAt: 1_000,
|
||||||
|
endedAt: 1_900,
|
||||||
|
runtimeMs: 900,
|
||||||
|
};
|
||||||
|
await fs.writeFile(storePath, JSON.stringify({ [sessionKey]: terminalEntry }, null, 2));
|
||||||
|
|
||||||
|
const staleInMemory: Record<string, SessionEntry> = {
|
||||||
|
[sessionKey]: {
|
||||||
|
sessionId,
|
||||||
|
updatedAt: 1_100,
|
||||||
|
status: "running",
|
||||||
|
startedAt: 1_000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
await updateSessionStoreAfterAgentRun({
|
||||||
|
cfg,
|
||||||
|
sessionId,
|
||||||
|
sessionKey,
|
||||||
|
storePath,
|
||||||
|
sessionStore: staleInMemory,
|
||||||
|
defaultProvider: "openai",
|
||||||
|
defaultModel: "gpt-5.4",
|
||||||
|
result: {
|
||||||
|
payloads: [],
|
||||||
|
meta: {
|
||||||
|
aborted: false,
|
||||||
|
agentMeta: {
|
||||||
|
provider: "openai",
|
||||||
|
model: "gpt-5.4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
});
|
||||||
|
|
||||||
|
const persisted = loadSessionStore(storePath, { skipCache: true })[sessionKey];
|
||||||
|
expect(persisted).toMatchObject({
|
||||||
|
status: "done",
|
||||||
|
startedAt: 1_000,
|
||||||
|
endedAt: 1_900,
|
||||||
|
runtimeMs: 900,
|
||||||
|
modelProvider: "openai",
|
||||||
|
model: "gpt-5.4",
|
||||||
|
});
|
||||||
|
expect(staleInMemory[sessionKey]?.status).toBe("done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("persists latest systemPromptReport for downstream warning dedupe", async () => {
|
it("persists latest systemPromptReport for downstream warning dedupe", async () => {
|
||||||
await withTempSessionStore(async ({ storePath }) => {
|
await withTempSessionStore(async ({ storePath }) => {
|
||||||
const sessionKey = "agent:codex:report:test-system-prompt-report";
|
const sessionKey = "agent:codex:report:test-system-prompt-report";
|
||||||
|
|||||||
@@ -36,6 +36,15 @@ function resolvePositiveInteger(value: number | undefined): number | undefined {
|
|||||||
return Math.floor(value);
|
return Math.floor(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeLifecycleStateFromMetadataPatch(entry: SessionEntry): SessionEntry {
|
||||||
|
const next = { ...entry };
|
||||||
|
delete next.status;
|
||||||
|
delete next.startedAt;
|
||||||
|
delete next.endedAt;
|
||||||
|
delete next.runtimeMs;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateSessionStoreAfterAgentRun(params: {
|
export async function updateSessionStoreAfterAgentRun(params: {
|
||||||
cfg: OpenClawConfig;
|
cfg: OpenClawConfig;
|
||||||
contextTokensOverride?: number;
|
contextTokensOverride?: number;
|
||||||
@@ -218,8 +227,9 @@ export async function updateSessionStoreAfterAgentRun(params: {
|
|||||||
if (compactionsThisRun > 0) {
|
if (compactionsThisRun > 0) {
|
||||||
next.compactionCount = (entry.compactionCount ?? 0) + compactionsThisRun;
|
next.compactionCount = (entry.compactionCount ?? 0) + compactionsThisRun;
|
||||||
}
|
}
|
||||||
|
const metadataPatch = removeLifecycleStateFromMetadataPatch(next);
|
||||||
const persisted = await updateSessionStore(storePath, (store) => {
|
const persisted = await updateSessionStore(storePath, (store) => {
|
||||||
const merged = mergeSessionEntry(store[sessionKey], next);
|
const merged = mergeSessionEntry(store[sessionKey], metadataPatch);
|
||||||
store[sessionKey] = merged;
|
store[sessionKey] = merged;
|
||||||
return merged;
|
return merged;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user