Skip to content
Open
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
1 change: 1 addition & 0 deletions apps/server/integration/TestProviderAdapter.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ export const makeTestProviderAdapterHarness = (options?: MakeTestProviderAdapter
readThread,
rollbackThread,
stopAll,
discoverSlashCommands: () => Effect.succeed([]),
streamEvents: Stream.fromQueue(runtimeEvents),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function makeSnapshot(input: {
workspaceRoot: input.workspaceRoot,
defaultModelSelection: null,
scripts: [],
cachedProviderSlashCommands: {},
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
deletedAt: null,
Expand Down
11 changes: 11 additions & 0 deletions apps/server/src/orchestration/Layers/CheckpointReactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function createProviderServiceHarness(
listSessions,
getCapabilities: () => Effect.succeed({ sessionModelSwitch: "in-session" }),
rollbackConversation,
discoverSlashCommands: () => Effect.succeed([]),
streamEvents: Stream.fromPubSub(runtimeEventPubSub),
};

Expand Down Expand Up @@ -355,6 +356,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -431,6 +433,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: asTurnId("turn-main"),
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -508,6 +511,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -566,6 +570,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -654,6 +659,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: asTurnId("turn-missing-cwd"),
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -701,6 +707,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -751,6 +758,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -803,6 +811,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -881,6 +890,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down Expand Up @@ -950,6 +960,7 @@ describe("CheckpointReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: createdAt,
},
createdAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function commandToAggregateRef(command: OrchestrationCommand): {
case "project.create":
case "project.meta.update":
case "project.delete":
case "project.provider-slash-commands.set":
return {
aggregateKind: "project",
aggregateId: command.projectId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,7 @@ it.effect("restores pending turn-start metadata across projection pipeline resta
runtimeMode: "approval-required",
activeTurnId: turnId,
lastError: null,
providerSlashCommands: [],
updatedAt: sessionSetAt,
},
},
Expand Down
26 changes: 26 additions & 0 deletions apps/server/src/orchestration/Layers/ProjectionPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
workspaceRoot: event.payload.workspaceRoot,
defaultModelSelection: event.payload.defaultModelSelection,
scripts: event.payload.scripts,
cachedProviderSlashCommandsJson: null,
createdAt: event.payload.createdAt,
updatedAt: event.payload.updatedAt,
deletedAt: null,
Expand Down Expand Up @@ -428,6 +429,26 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
return;
}

case "project.provider-slash-commands-set": {
const existingRow = yield* projectionProjectRepository.getById({
projectId: event.payload.projectId,
});
if (Option.isNone(existingRow)) {
return;
}
// Merge new provider commands into existing cached map.
const existingCache: Record<string, unknown> = existingRow.value.cachedProviderSlashCommandsJson
? JSON.parse(existingRow.value.cachedProviderSlashCommandsJson)
: {};
existingCache[event.payload.provider] = event.payload.commands;
yield* projectionProjectRepository.upsert({
...existingRow.value,
cachedProviderSlashCommandsJson: JSON.stringify(existingCache),
updatedAt: event.payload.updatedAt,
});
return;
}

default:
return;
}
Expand Down Expand Up @@ -803,13 +824,18 @@ const makeOrchestrationProjectionPipeline = Effect.fn("makeOrchestrationProjecti
if (event.type !== "thread.session-set") {
return;
}
const providerSlashCommands = event.payload.session.providerSlashCommands;
yield* projectionThreadSessionRepository.upsert({
threadId: event.payload.threadId,
status: event.payload.session.status,
providerName: event.payload.session.providerName,
runtimeMode: event.payload.session.runtimeMode,
activeTurnId: event.payload.session.activeTurnId,
lastError: event.payload.session.lastError,
providerSlashCommandsJson:
providerSlashCommands && providerSlashCommands.length > 0
? JSON.stringify(providerSlashCommands)
: null,
updatedAt: event.payload.session.updatedAt,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
runOnWorktreeCreate: false,
},
],
cachedProviderSlashCommands: {},
createdAt: "2026-02-24T00:00:00.000Z",
updatedAt: "2026-02-24T00:00:01.000Z",
deletedAt: null,
Expand Down Expand Up @@ -332,6 +333,7 @@ projectionSnapshotLayer("ProjectionSnapshotQuery", (it) => {
runtimeMode: "approval-required",
activeTurnId: asTurnId("turn-1"),
lastError: null,
providerSlashCommands: [],
updatedAt: "2026-02-24T00:00:07.000Z",
},
},
Expand Down
51 changes: 40 additions & 11 deletions apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ const ProjectionThreadActivityDbRowSchema = ProjectionThreadActivity.mapFields(
sequence: Schema.NullOr(NonNegativeInt),
}),
);
const ProjectionThreadSessionDbRowSchema = ProjectionThreadSession;
const ProjectionThreadSessionDbRowSchema = ProjectionThreadSession.mapFields(
Struct.assign({
providerSlashCommandsJson: Schema.NullOr(Schema.String),
}),
);
const ProjectionCheckpointDbRowSchema = ProjectionCheckpoint.mapFields(
Struct.assign({
files: Schema.fromJsonString(Schema.Array(OrchestrationCheckpointFile)),
Expand Down Expand Up @@ -149,6 +153,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
workspace_root AS "workspaceRoot",
default_model_selection_json AS "defaultModelSelection",
scripts_json AS "scripts",
cached_provider_slash_commands_json AS "cachedProviderSlashCommandsJson",
created_at AS "createdAt",
updated_at AS "updatedAt",
deleted_at AS "deletedAt"
Expand Down Expand Up @@ -259,6 +264,7 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
runtime_mode AS "runtimeMode",
active_turn_id AS "activeTurnId",
last_error AS "lastError",
provider_slash_commands_json AS "providerSlashCommandsJson",
updated_at AS "updatedAt"
FROM projection_thread_sessions
ORDER BY thread_id ASC
Expand Down Expand Up @@ -527,27 +533,50 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {

for (const row of sessionRows) {
updatedAt = maxIso(updatedAt, row.updatedAt);
const providerSlashCommands: Array<string> = (() => {
const json = row.providerSlashCommandsJson;
if (!json) return [];
try {
const parsed: unknown = JSON.parse(json);
return Array.isArray(parsed) ? (parsed as Array<string>) : [];
} catch {
return [];
}
})();
sessionsByThread.set(row.threadId, {
threadId: row.threadId,
status: row.status,
providerName: row.providerName,
runtimeMode: row.runtimeMode,
activeTurnId: row.activeTurnId,
lastError: row.lastError,
providerSlashCommands,
updatedAt: row.updatedAt,
});
}

const projects: ReadonlyArray<OrchestrationProject> = projectRows.map((row) => ({
id: row.projectId,
title: row.title,
workspaceRoot: row.workspaceRoot,
defaultModelSelection: row.defaultModelSelection,
scripts: row.scripts,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
deletedAt: row.deletedAt,
}));
const projects: ReadonlyArray<OrchestrationProject> = projectRows.map((row) => {
const cachedProviderSlashCommands: OrchestrationProject["cachedProviderSlashCommands"] =
(() => {
if (!row.cachedProviderSlashCommandsJson) return {};
try {
return JSON.parse(row.cachedProviderSlashCommandsJson) as OrchestrationProject["cachedProviderSlashCommands"];
} catch {
return {};
}
})();
return {
id: row.projectId,
title: row.title,
workspaceRoot: row.workspaceRoot,
defaultModelSelection: row.defaultModelSelection,
scripts: row.scripts,
cachedProviderSlashCommands,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
deletedAt: row.deletedAt,
};
});

const threads: ReadonlyArray<OrchestrationThread> = threadRows.map((row) => ({
id: row.threadId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ describe("ProviderCommandReactor", () => {
sessionModelSwitch: input?.sessionModelSwitch ?? "in-session",
}),
rollbackConversation: () => unsupported(),
discoverSlashCommands: () => Effect.succeed([]),
streamEvents: Stream.fromPubSub(runtimeEventPubSub),
};

Expand Down Expand Up @@ -1038,6 +1039,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "full-access",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down Expand Up @@ -1222,6 +1224,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "approval-required",
activeTurnId: asTurnId("turn-1"),
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down Expand Up @@ -1260,6 +1263,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down Expand Up @@ -1301,6 +1305,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down Expand Up @@ -1355,6 +1360,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down Expand Up @@ -1452,6 +1458,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down Expand Up @@ -1554,6 +1561,7 @@ describe("ProviderCommandReactor", () => {
runtimeMode: "approval-required",
activeTurnId: null,
lastError: null,
providerSlashCommands: [],
updatedAt: now,
},
createdAt: now,
Expand Down
Loading
Loading