mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-08 19:19:42 +00:00
* feat(opencode): support plan mode * feat(opencode): support reasoning effort * feat(opencode): surface context usage in web Bridge OpenCode ACP usage updates into the existing token-count pipeline so the web status bar can show live context and cache information without a separate UI path. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(opencode): restrict plan mode to remote, rollback reasoning effort on failure - Block local OpenCode plan startup (tools not enforced in local path) - Allow remote OpenCode plan only (ACP permission handler denies tools) - Guard web /permission-mode endpoint for local OpenCode plan sessions - Rollback session reasoning effort when OpenCode rejects set_config_option - Wire rollback callback through opencodeLoop to runOpencode closure - Add tests: local plan rejected, remote plan allowed, web guard, effort rollback * fix(web): auto-retry OpenCode models query to populate model selector without refresh - Retry early failures (RPC may still be registering on new sessions) - Poll briefly until availableModels is non-empty - Stop polling once model options are discovered - Add tests for retry/poll/stop policy * fix(opencode): cap model discovery polling --------- Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import { MessageQueue2 } from '@/utils/MessageQueue2';
|
|
import { logger } from '@/ui/logger';
|
|
import { runLocalRemoteSession } from '@/agent/loopBase';
|
|
import { OpencodeSession } from './session';
|
|
import { opencodeLocalLauncher } from './opencodeLocalLauncher';
|
|
import { opencodeRemoteLauncher } from './opencodeRemoteLauncher';
|
|
import { ApiClient, ApiSessionClient } from '@/lib';
|
|
import type { OpencodeMode, PermissionMode } from './types';
|
|
import type { OpencodeHookServer } from './utils/startOpencodeHookServer';
|
|
|
|
interface OpencodeLoopOptions {
|
|
path: string;
|
|
startingMode?: 'local' | 'remote';
|
|
startedBy?: 'runner' | 'terminal';
|
|
onModeChange: (mode: 'local' | 'remote') => void;
|
|
messageQueue: MessageQueue2<OpencodeMode>;
|
|
session: ApiSessionClient;
|
|
api: ApiClient;
|
|
permissionMode?: PermissionMode;
|
|
model?: string;
|
|
modelReasoningEffort?: string | null;
|
|
resumeSessionId?: string;
|
|
hookServer: OpencodeHookServer;
|
|
hookUrl: string;
|
|
onSessionReady?: (session: OpencodeSession) => void;
|
|
onReasoningEffortRollback?: (effort: string | null) => void;
|
|
}
|
|
|
|
export async function opencodeLoop(opts: OpencodeLoopOptions): Promise<void> {
|
|
const logPath = logger.getLogPath();
|
|
const startedBy = opts.startedBy ?? 'terminal';
|
|
const startingMode = opts.startingMode ?? 'local';
|
|
|
|
const session = new OpencodeSession({
|
|
api: opts.api,
|
|
client: opts.session,
|
|
path: opts.path,
|
|
sessionId: opts.resumeSessionId ?? null,
|
|
logPath,
|
|
messageQueue: opts.messageQueue,
|
|
onModeChange: opts.onModeChange,
|
|
mode: startingMode,
|
|
startedBy,
|
|
startingMode,
|
|
permissionMode: opts.permissionMode ?? 'default',
|
|
modelReasoningEffort: opts.modelReasoningEffort
|
|
});
|
|
|
|
if (opts.resumeSessionId) {
|
|
session.onSessionFound(opts.resumeSessionId);
|
|
}
|
|
|
|
await runLocalRemoteSession({
|
|
session,
|
|
startingMode: opts.startingMode,
|
|
logTag: 'opencode-loop',
|
|
runLocal: (instance) => opencodeLocalLauncher(instance, {
|
|
hookServer: opts.hookServer,
|
|
hookUrl: opts.hookUrl
|
|
}),
|
|
runRemote: (instance) => opencodeRemoteLauncher(instance, {
|
|
onReasoningEffortRollback: opts.onReasoningEffortRollback
|
|
}),
|
|
onSessionReady: opts.onSessionReady
|
|
});
|
|
}
|