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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ Slow reasoning models (or very large profile prompts) can exceed the default 90

Supported providers: any provider listed by `opencode providers list` (e.g. `anthropic`, `openai`, `github-copilot`, ...).

If `opencodeProvider` and `opencodeModel` are set, they take precedence over the manual `memoryProvider` settings below.
If `opencodeProvider` and `opencodeModel` are set, they take precedence over the manual `memoryProvider` settings below by default (`"autoCaptureProviderOrder": "opencode-first"`). Set `"autoCaptureProviderOrder": "external-first"` to prefer the manual API when both are configured (useful when the host session API is busy/slow); the other channel is used as fallback. Lower `"opencodeTimeoutMs"` to fail over from the opencode channel sooner.

**Follow the session model:** set `"opencodeModel": "inherit"` to use a concrete OpenCode model at call time instead of a pinned id. For **auto-capture**, each prompt is recorded via the `chat.params` hook and the capture request reuses that prompt's provider/model. For **profile learning** and other structured-output paths (which are not tied to a single user message), `inherit` falls back to the most recent model in OpenCode's `model.json` recent list (preferring the configured `opencodeProvider`). Sending the literal model id `inherit` is never valid and previously caused `ProviderModelNotFoundError: Model not found: <provider>/inherit` on those paths. `opencodeProvider` is still required as the normal config gate.

Expand Down
7 changes: 7 additions & 0 deletions opencode-mem.example.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@

"autoCaptureEnabled": true,

// When both opencodeProvider and manual memoryModel/memoryApiUrl are set, which
// channel to try first for auto-capture summaries. Default "opencode-first".
// Prefer "external-first" if the host session API is slow/busy and you want the
// faster external API path. Failed primary falls back to the other channel.
// Faster opencode failover: also lower "opencodeTimeoutMs" (default 90000).
// "autoCaptureProviderOrder": "external-first",

// Provider type: "atlas-cloud" | "cheaper-inference" | "openai-chat" | "openai-responses" | "anthropic" | "minimax" | "orcarouter"
// Note: "openai-chat" is a generic OpenAI API-compatible mode.
// Any service that follows the OpenAI Chat Completions API can use it via custom "memoryApiUrl".
Expand Down
25 changes: 25 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ interface OpenCodeMemConfig {
autoCaptureMaxRetries?: number;
autoCaptureMaxContextBytes?: number;
autoCaptureLanguage?: string;
/**
* Which auto-capture summary channel to try first when both opencode and
* manual (memoryModel/memoryApiUrl) providers are configured.
* Default "opencode-first". Use "external-first" for lower latency when the
* host session API is busy; failed primary channel falls back to the other.
*/
autoCaptureProviderOrder?: "opencode-first" | "external-first";
memoryProvider?:
| "atlas-cloud"
| "cheaper-inference"
Expand Down Expand Up @@ -256,6 +263,7 @@ const DEFAULTS: Required<
autoCaptureIterationTimeout: 30000,
autoCaptureMaxRetries: 3,
autoCaptureMaxContextBytes: 131072,
autoCaptureProviderOrder: "opencode-first",
aiSessionRetentionDays: 7,
webServerEnabled: true,
webServerPort: 4747,
Expand Down Expand Up @@ -600,6 +608,13 @@ const CONFIG_TEMPLATE = `{
// Prevents HTTP 400 context overflows on models with ~131K token windows (e.g. Groq Llama).
// Rough guide: tokens ≈ bytes / 4 for mixed code/prose.
"autoCaptureMaxContextBytes": 131072,

// When both opencodeProvider and manual memoryModel/memoryApiUrl are set, which
// channel to try first for auto-capture summaries. Default "opencode-first".
// Prefer "external-first" if the host session API is slow/busy (~15–50s) and
// you want the faster external API path (~3s). Failed primary falls back.
// Faster opencode failover also: lower "opencodeTimeoutMs" (default 90000).
// "autoCaptureProviderOrder": "external-first",

// Days to keep AI session history before cleanup
"aiSessionRetentionDays": 7,
Expand Down Expand Up @@ -899,6 +914,13 @@ export function normalizeAutoCaptureMaxContextBytes(value: number): number {
return value;
}

export type AutoCaptureProviderOrder = "opencode-first" | "external-first";

/** Invalid / unset values fall back to the historical default (opencode-first). */
export function normalizeAutoCaptureProviderOrder(value: unknown): AutoCaptureProviderOrder {
return value === "external-first" ? "external-first" : "opencode-first";
}

export function normalizeAutoCleanupRetentionDays(value: number): number {
if (!Number.isSafeInteger(value) || value <= 0) {
throw new Error(`Invalid autoCleanupRetentionDays config: ${value}`);
Expand Down Expand Up @@ -1059,6 +1081,9 @@ function buildConfig(fileConfig: OpenCodeMemConfig) {
autoCaptureMaxRetries: fileConfig.autoCaptureMaxRetries ?? DEFAULTS.autoCaptureMaxRetries,
autoCaptureMaxContextBytes,
autoCaptureLanguage: fileConfig.autoCaptureLanguage,
autoCaptureProviderOrder: normalizeAutoCaptureProviderOrder(
fileConfig.autoCaptureProviderOrder
),
memoryProvider,
memoryModel: fileConfig.memoryModel,
memoryApiUrl: fileConfig.memoryApiUrl,
Expand Down
7 changes: 6 additions & 1 deletion src/services/api-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,12 @@ export async function handleUnpinMemory(id: string): Promise<ApiResponse<void>>
}

export async function handleRunCleanup(): Promise<
ApiResponse<{ deletedCount: number; userCount: number; projectCount: number }>
ApiResponse<{
deletedCount: number;
userCount: number;
projectCount: number;
aiSessionsDeleted?: number;
}>
> {
try {
const { cleanupService } = await import("./cleanup-service.js");
Expand Down
Loading
Loading