Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions apps/desktop/src/main/__tests__/app-shell-session-purge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function summary(id: string, overrides: Partial<SessionSummary> = {}): SessionSu
isArchived: true,
labels: [],
hasUnread: false,
status: 'archived',
status: 'active',
backend: 'fake',
llmConnectionSlug: 'test',
connectionLocked: true,
Expand All @@ -21,14 +21,9 @@ function summary(id: string, overrides: Partial<SessionSummary> = {}): SessionSu
};
}

/**
* A task that left the archive. Both fields move together because that is what
* `SessionStore.unarchive` writes; flipping only `isArchived` would build a row
* the store cannot produce, and the sweep would then be tested against a state
* it will never meet.
*/
/** A task that left the archive while retaining its independent execution status. */
function restored(id: string): SessionSummary {
return summary(id, { isArchived: false, status: 'active' });
return summary(id, { isArchived: false });
}

type SweepHarness = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import type { StoredMessage } from '@maka/core/session';
import { SESSION_CONTINUITY_SCHEMA_VERSION } from '@maka/runtime-host/protocol';
import {
encodeDesktopTranscriptChange,
encodeDesktopTranscriptSnapshot,
Expand Down Expand Up @@ -546,7 +547,7 @@ function transcriptPage(

function continuitySnapshot() {
return {
schemaVersion: 3 as const,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: 'session-1',
metadataRevision: 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { RuntimeHostOperationError } from '@maka/runtime-host/client';
import type {
SessionCatalogProjection,
SessionContinuitySnapshot,
SubscriptionFrame,
TurnSnapshot,
import {
SESSION_CONTINUITY_SCHEMA_VERSION,
type SessionCatalogProjection,
type SessionContinuitySnapshot,
type SubscriptionFrame,
type TurnSnapshot,
} from '@maka/runtime-host/protocol';
import { BotSessionUnavailableError } from '../bot-session-adapter.js';
import {
Expand Down Expand Up @@ -87,6 +88,39 @@ test('prepares a bound Session without exposing Host configuration revisions to
);
});

test('rejects archived Sessions before and after a permission transition', async () => {
const initiallyArchived = createRuntimeHostBotSessionAdapter({
client: botClient({
getSession: async () =>
session('bot-session-1', { isArchived: true, status: 'active' }),
}),
resolveCreateTarget: hostPathCreateTarget,
emitSessionsChanged() {},
});
await assert.rejects(
initiallyArchived.prepareSession('bot-session-1'),
BotSessionUnavailableError,
);

const archivedDuringUpdate = createRuntimeHostBotSessionAdapter({
client: botClient({
getSession: async () => session('bot-session-1', { permissionMode: 'ask' }),
updateSessionConfiguration: async (sessionId) =>
session(sessionId, {
permissionMode: 'explore',
isArchived: true,
status: 'active',
}),
}),
resolveCreateTarget: hostPathCreateTarget,
emitSessionsChanged() {},
});
await assert.rejects(
archivedDuringUpdate.prepareSession('bot-session-1'),
BotSessionUnavailableError,
);
});

test('reconciles an uncertain Host Session create with its stable Session identity', async () => {
const adapter = createRuntimeHostBotSessionAdapter({
client: botClient({
Expand Down Expand Up @@ -401,7 +435,7 @@ function startedTurn(turn: TurnSnapshot) {

function continuitySnapshot(rootTurn: TurnSnapshot | null): SessionContinuitySnapshot {
return {
schemaVersion: 3,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: 'bot-session-1',
metadataRevision: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ test('drives the renderer Session catalog facade through real UDS framing', asyn
...projected,
revision: projected.revision + 1,
isArchived: archived,
status: archived ? 'archived' : 'active',
});
return { ok: true, result: projected };
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import type { BotIncomingMessage, BotRegistry } from '@maka/runtime/bots';
import type { ComputerUseToolSet } from '@maka/runtime/computer-use-tools';
import type { MakaTool } from '@maka/runtime/tool-runtime';
import type { ClientCapabilityProvider, RuntimeHostConnection } from '@maka/runtime-host/client';
import type {
ClientCapabilityCallFrame,
OperationInput,
OperationKey,
SessionAssistantStreamIdentity,
SessionCatalogProjection,
SessionContinuitySnapshot,
SubscriptionFrame,
import {
SESSION_CONTINUITY_SCHEMA_VERSION,
type ClientCapabilityCallFrame,
type OperationInput,
type OperationKey,
type SessionAssistantStreamIdentity,
type SessionCatalogProjection,
type SessionContinuitySnapshot,
type SubscriptionFrame,
} from '@maka/runtime-host/protocol';
import { z } from 'zod';
import { createAttachmentApprovalRegistry } from '../attachment-approval.js';
Expand Down Expand Up @@ -988,7 +989,7 @@ function continuitySnapshot(
overrides: Partial<SessionContinuitySnapshot> = {},
): SessionContinuitySnapshot {
return {
schemaVersion: 3,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: 'session-1',
metadataRevision: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import test from "node:test";
import type { IpcMain } from "electron";
import { SIDE_CONVERSATION_SESSION_LABEL } from '@maka/core/side-conversation';
import { type AttachmentRef } from '@maka/core/events';
import {
SESSION_CONTINUITY_SCHEMA_VERSION,
type SessionCatalogProjection,
} from "@maka/runtime-host/protocol";
import { RuntimeHostOperationError } from '@maka/runtime-host/client';
import type { SessionCatalogProjection } from "@maka/runtime-host/protocol";
import { createAttachmentApprovalRegistry } from "../attachment-approval.js";
import type { DesktopRuntimeHostSession } from "../runtime-host-client.js";
import {
Expand Down Expand Up @@ -784,7 +787,7 @@ function observerWithTranscript(
client: {
openSession: async () => runtimeHostSessionFixture({
snapshot: {
schemaVersion: 3,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: "session-1",
metadataRevision: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { EventEmitter } from "node:events";
import test from "node:test";
import type { SessionEvent } from '@maka/core/events';
import type { StoredMessage } from '@maka/core/session';
import type {
SessionContinuitySnapshot,
SubscriptionFrame,
import {
SESSION_CONTINUITY_SCHEMA_VERSION,
type SessionContinuitySnapshot,
type SubscriptionFrame,
} from "@maka/runtime-host/protocol";
import { RuntimeHostSubscriptionError } from "@maka/runtime-host/client";
import type { DesktopRuntimeHostSession } from "../runtime-host-client.js";
Expand Down Expand Up @@ -2246,7 +2247,7 @@ function continuitySnapshot(
overrides: Partial<SessionContinuitySnapshot> = {},
): SessionContinuitySnapshot {
return {
schemaVersion: 3,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: "session-1",
metadataRevision: 1,
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/main/runtime-host-bot-session-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function createRuntimeHostBotSessionAdapter(
throwUnavailable(error, sessionId);
throw error;
}
if (!session || session.isArchived || session.status === 'archived') {
if (!session || session.isArchived) {
throw unavailableSession(sessionId);
}
if (session.permissionMode === 'explore') return 'ready';
Expand All @@ -96,7 +96,7 @@ export function createRuntimeHostBotSessionAdapter(
if (isPermissionUpdateRefusal(error)) return 'permission_refused';
throw error;
}
if (session.isArchived || session.status === 'archived') {
if (session.isArchived) {
throw unavailableSession(sessionId);
}
if (session.permissionMode !== 'explore') return 'permission_refused';
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/stories/settings/settings-pages.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ function archivedTask(
isArchived: true,
labels: [],
hasUnread: false,
status: 'archived',
status: 'active',
backend: 'ai-sdk',
llmConnectionSlug: 'zai-live',
connectionLocked: true,
Expand Down
17 changes: 9 additions & 8 deletions packages/cli/src/__tests__/runtime-host-session-driver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import type {
RuntimeHostSessionSubscription,
} from '@maka/runtime-host/client';
import { RuntimeHostSubscriptionError } from '@maka/runtime-host/client';
import type {
InteractionPendingSnapshot,
OperationInput,
OperationOutput,
SessionCatalogProjection,
SessionContinuitySnapshot,
SubscriptionFrame,
import {
SESSION_CONTINUITY_SCHEMA_VERSION,
type InteractionPendingSnapshot,
type OperationInput,
type OperationOutput,
type SessionCatalogProjection,
type SessionContinuitySnapshot,
type SubscriptionFrame,
} from '@maka/runtime-host/protocol';
import {
createRuntimeHostMakaSessionDriver,
Expand Down Expand Up @@ -1425,7 +1426,7 @@ function continuitySnapshot(
overrides: Partial<SessionContinuitySnapshot> = {},
): SessionContinuitySnapshot {
return {
schemaVersion: 3,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: 'session-1',
metadataRevision: 1,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/__tests__/session-status.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ describe('session status contract', () => {
'running',
'waiting_for_user',
'blocked',
'archived',
'aborted',
]);
for (const status of SESSION_STATUSES) {
assert.equal(isSessionStatus(status), true);
}
assert.equal(isSessionStatus('review'), false);
assert.equal(isSessionStatus('done'), false);
assert.equal(isSessionStatus('archived'), false);
});
});
13 changes: 5 additions & 8 deletions packages/core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,12 @@ import { decodeTurnOrigin, type TurnOrigin } from './turn-origin.js';

export { DEEP_RESEARCH_SESSION_LABEL, isDeepResearchSession } from './explore-agent.js';

/**
* `archived` is still here and still written by `SessionStore.archive()`
* alongside `isArchived`; consolidating those two onto one authority is its own
* change (#2984, PR 3) because it rewrites stored rows.
*
*/
/** Runtime execution states. Archive visibility is represented by `isArchived`. */
export const SESSION_STATUSES = [
'active',
'running',
'waiting_for_user',
'blocked',
'archived',
'aborted',
] as const;

Expand Down Expand Up @@ -206,7 +200,6 @@ export interface SessionHeader {
labels: string[];

isArchived: boolean;
archivedAt?: number;
status: SessionStatus;
blockedReason?: SessionBlockedReason;
statusUpdatedAt?: number;
Expand Down Expand Up @@ -262,6 +255,10 @@ export interface SessionHeader {
schemaVersion: 1;
}

export type SessionHeaderPatch = Partial<Omit<SessionHeader, 'isArchived'>> & {
readonly isArchived?: never;
};

export type BackendKind = 'ai-sdk' | 'fake';

export interface SessionSummary {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createSessionStore } from '@maka/storage';
import { ClientSessionSubscription } from '../dist/client/session-subscription.js';
import { SESSION_CONTINUITY_SCHEMA_VERSION } from '../dist/protocol/index.js';
import {
createSessionTranscriptBootstrap,
readSessionTranscriptPage,
Expand Down Expand Up @@ -62,7 +63,7 @@ async function runFixture(fixture) {
activeAssistantStreams: [],
transcript: bootstrap,
snapshot: {
schemaVersion: 3,
schemaVersion: SESSION_CONTINUITY_SCHEMA_VERSION,
session: {
sessionId: state.sessionId,
metadataRevision: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,11 @@ export class ExecutionFixture {
let stores: Awaited<ReturnType<typeof openInteractiveExecutionStoresForWrite>> | undefined;
try {
stores = await openInteractiveExecutionStoresForWrite(owner.lease);
await stores.sessionStore.archive(this.sessionId);
const current = await stores.sessionStore.readHeaderRecordSnapshot(this.sessionId);
await stores.sessionStore.setSessionsArchivedVersioned(
[{ sessionId: this.sessionId, expectedVersion: current.revision }],
true,
);
} finally {
await stores?.sessionStore.close?.();
await owner.close();
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime-host/src/__tests__/goal-coordinator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,9 @@ test('session retirement forgets a terminal Goal without recreating deleted auth

const retirement = await coordinator.beginSessionRetirement([session.id], 'archive');
const header = await stores.sessionStore.readHeaderRecordSnapshot(session.id);
await stores.sessionStore.setSessionsLifecycleVersioned(
await stores.sessionStore.setSessionsArchivedVersioned(
[{ sessionId: session.id, expectedVersion: header.revision }],
'archived',
true,
);
retirement.commit();

Expand Down
Loading
Loading