Initial public release

This commit is contained in:
Bohdan Triapitsyn
2025-12-07 19:32:53 +02:00
commit 4b2edf7318
319 changed files with 81600 additions and 0 deletions
@@ -0,0 +1,22 @@
export const calculateContextUsage = (
totalTokens: number,
contextLimit: number,
outputLimit: number
) => {
const safeContext = Number.isFinite(contextLimit) ? Math.max(contextLimit, 0) : 0;
const hasOutputLimit = Number.isFinite(outputLimit) && outputLimit > 0;
const safeOutput = hasOutputLimit ? Math.max(outputLimit, 0) : 0;
const effectiveOutputReservation = Math.min(hasOutputLimit ? safeOutput : 32000, 32000);
const normalizedOutput = Math.min(effectiveOutputReservation, safeContext);
const thresholdLimit = safeContext > 0 ? Math.max(safeContext - normalizedOutput, 1) : 0;
const percentage = thresholdLimit > 0 ? (totalTokens / thresholdLimit) * 100 : 0;
return {
percentage: Math.min(percentage, 100),
contextLimit: safeContext,
outputLimit: safeOutput,
thresholdLimit: thresholdLimit || 1,
normalizedOutput
};
};
@@ -0,0 +1,68 @@
import type { Part } from "@opencode-ai/sdk";
const extractTextFromDelta = (delta: unknown): string => {
if (!delta) return '';
if (typeof delta === 'string') return delta;
if (Array.isArray(delta)) {
return delta.map((item) => extractTextFromDelta(item)).join('');
}
if (typeof delta === 'object') {
if (typeof (delta as { text?: unknown }).text === 'string') {
return (delta as { text: string }).text;
}
if (Array.isArray((delta as { content?: unknown[] }).content)) {
return (delta as { content: unknown[] }).content.map((item: unknown) => extractTextFromDelta(item)).join('');
}
}
return '';
};
export const extractTextFromPart = (part: unknown): string => {
if (!part) return '';
const typedPart = part as { text?: string | unknown[]; delta?: unknown; content?: string | unknown[] };
if (typeof typedPart.text === 'string') return typedPart.text;
if (Array.isArray(typedPart.text)) {
return typedPart.text.map((item: unknown) => (typeof item === 'string' ? item : extractTextFromPart(item))).join('');
}
const deltaText = extractTextFromDelta(typedPart.delta);
if (deltaText) return deltaText;
if (typeof typedPart.content === 'string') return typedPart.content;
if (Array.isArray(typedPart.content)) {
return typedPart.content
.map((item: unknown) => {
if (typeof item === 'string') return item;
if (item && typeof item === 'object') {
const typedItem = item as { text?: string; delta?: unknown };
return typedItem.text || extractTextFromDelta(typedItem.delta) || '';
}
return '';
})
.join('');
}
return '';
};
export const normalizeStreamingPart = (incoming: Part, existing?: Part): Part => {
const normalized: { type?: string; text?: string; delta?: unknown; [key: string]: unknown } = { ...incoming } as { type?: string; text?: string; delta?: unknown; [key: string]: unknown };
normalized.type = normalized.type || 'text';
if (normalized.type === 'text') {
const existingText = existing && typeof (existing as { text?: string }).text === 'string' ? (existing as { text: string }).text : '';
const directText = typeof normalized.text === 'string' ? normalized.text : '';
const deltaText = extractTextFromDelta((incoming as { delta?: unknown }).delta);
if (directText) {
normalized.text = directText;
} else if (deltaText) {
normalized.text = existingText ? `${existingText}${deltaText}` : deltaText;
} else if (existingText) {
normalized.text = existingText;
} else {
normalized.text = '';
}
delete normalized.delta;
}
return normalized as Part;
};
@@ -0,0 +1,54 @@
import type { EditPermissionMode } from "../types/sessionTypes";
const EDIT_PERMISSION_TOOL_NAMES = new Set([
'edit',
'multiedit',
'str_replace',
'str_replace_based_edit_tool',
'write',
]);
export const isEditPermissionType = (type?: string | null): boolean => {
if (!type) {
return false;
}
return EDIT_PERMISSION_TOOL_NAMES.has(type.toLowerCase());
};
const resolveConfigStore = () => {
if (typeof window === 'undefined') {
return undefined;
}
return (window as { __zustand_config_store__?: { getState?: () => { agents?: Array<{ name: string; permission?: { edit?: string }; tools?: { edit?: boolean } }> } } }).__zustand_config_store__;
};
const getAgentDefinition = (agentName?: string): { name: string; permission?: { edit?: string }; tools?: { edit?: boolean } } | undefined => {
if (!agentName) {
return undefined;
}
try {
const configStore = resolveConfigStore();
if (configStore?.getState) {
const state = configStore.getState();
return state.agents?.find?.((agent: { name: string; permission?: { edit?: string }; tools?: { edit?: boolean } }) => agent.name === agentName);
}
} catch { /* ignored */ }
return undefined;
};
export const getAgentDefaultEditPermission = (agentName?: string): EditPermissionMode => {
const agent = getAgentDefinition(agentName);
if (!agent) {
return 'ask';
}
const permission = agent.permission?.edit;
if (permission === 'allow' || permission === 'ask' || permission === 'deny' || permission === 'full') {
return permission;
}
const editToolEnabled = agent.tools ? agent.tools.edit !== false : true;
return editToolEnabled ? 'ask' : 'deny';
};
+121
View File
@@ -0,0 +1,121 @@
let safeStorageInstance: Storage | null = null;
const createInMemoryStorage = (): Storage => {
const store = new Map<string, string>();
return {
getItem: (key: string) => store.get(key) ?? null,
setItem: (key: string, value: string) => {
store.set(key, value);
},
removeItem: (key: string) => {
store.delete(key);
},
clear: () => {
store.clear();
},
key: (index: number) => Array.from(store.keys())[index] ?? null,
get length() {
return store.size;
},
} as Storage;
};
const createSafeStorage = (): Storage => {
if (typeof window === 'undefined' || !window.localStorage) {
return createInMemoryStorage();
}
const baseStorage = window.localStorage;
const fallback = createInMemoryStorage();
let storageAvailable = true;
const disableStorage = () => {
storageAvailable = false;
};
const safeGet = (key: string): string | null => {
if (storageAvailable) {
try {
const value = baseStorage.getItem(key);
if (value !== null) {
return value;
}
} catch {
disableStorage();
}
}
return fallback.getItem(key);
};
const safeSet = (key: string, value: string) => {
if (storageAvailable) {
try {
baseStorage.setItem(key, value);
fallback.removeItem(key);
return;
} catch {
disableStorage();
}
}
fallback.setItem(key, value);
};
const safeRemove = (key: string) => {
if (storageAvailable) {
try {
baseStorage.removeItem(key);
} catch {
disableStorage();
}
}
fallback.removeItem(key);
};
const safeClear = () => {
if (storageAvailable) {
try {
baseStorage.clear();
} catch {
disableStorage();
}
}
fallback.clear();
};
const safeKey = (index: number): string | null => {
if (storageAvailable) {
try {
return baseStorage.key(index);
} catch {
disableStorage();
}
}
return fallback.key(index);
};
return {
getItem: safeGet,
setItem: safeSet,
removeItem: safeRemove,
clear: safeClear,
key: safeKey,
get length() {
if (storageAvailable) {
try {
return baseStorage.length + fallback.length;
} catch {
disableStorage();
}
}
return fallback.length;
},
} as Storage;
};
export const getSafeStorage = (): Storage => {
if (!safeStorageInstance) {
safeStorageInstance = createSafeStorage();
}
return safeStorageInstance;
};
@@ -0,0 +1,8 @@
export const streamDebugEnabled = (): boolean => {
if (typeof window === 'undefined') return false;
try {
return window.localStorage.getItem('openchamber_stream_debug') === '1';
} catch {
return false;
}
};
@@ -0,0 +1,56 @@
import type { MessageStreamLifecycle } from "../types/sessionTypes";
export type { MessageStreamLifecycle };
export const touchStreamingLifecycle = (
source: Map<string, MessageStreamLifecycle>,
messageId: string
): Map<string, MessageStreamLifecycle> => {
const now = Date.now();
const existing = source.get(messageId);
const next = new Map(source);
next.set(messageId, {
phase: 'streaming',
startedAt: existing?.startedAt ?? now,
lastUpdateAt: now,
});
return next;
};
export const removeLifecycleEntries = (
source: Map<string, MessageStreamLifecycle>,
ids: Iterable<string>
): Map<string, MessageStreamLifecycle> => {
const idsArray = Array.from(ids);
const shouldClone = idsArray.some((id) => source.has(id));
if (!shouldClone) {
return source;
}
const next = new Map(source);
idsArray.forEach((id) => {
next.delete(id);
});
return next;
};
const lifecycleCompletionTimers = new Map<string, ReturnType<typeof setTimeout>>();
export const clearLifecycleCompletionTimer = (messageId: string) => {
const timer = lifecycleCompletionTimers.get(messageId);
if (timer) {
clearTimeout(timer);
lifecycleCompletionTimers.delete(messageId);
}
};
export const clearLifecycleTimersForIds = (ids: Iterable<string>) => {
for (const id of ids) {
clearLifecycleCompletionTimer(id);
}
};
@@ -0,0 +1,51 @@
import type { Message, Part } from "@opencode-ai/sdk";
type TokenBreakdown = {
input?: number;
output?: number;
reasoning?: number;
cache?: {
read?: number;
write?: number;
};
};
const sumTokenBreakdown = (breakdown: TokenBreakdown | null | undefined): number => {
if (!breakdown || typeof breakdown !== 'object') {
return 0;
}
const inputTokens = breakdown.input ?? 0;
const outputTokens = breakdown.output ?? 0;
const reasoningTokens = breakdown.reasoning ?? 0;
const cacheReadTokens = breakdown.cache && typeof breakdown.cache === 'object' ? breakdown.cache.read ?? 0 : 0;
const cacheWriteTokens = breakdown.cache && typeof breakdown.cache === 'object' ? breakdown.cache.write ?? 0 : 0;
return inputTokens + outputTokens + reasoningTokens + cacheReadTokens + cacheWriteTokens;
};
export const extractTokensFromMessage = (message: { info: Message; parts: Part[] }): number => {
const tokens = (message.info as { tokens?: number | TokenBreakdown }).tokens;
if (typeof tokens === 'number') {
return tokens;
}
if (tokens && typeof tokens === 'object') {
return sumTokenBreakdown(tokens);
}
const tokenPart = message.parts.find(
(part) => typeof (part as { tokens?: number | TokenBreakdown }).tokens !== 'undefined'
) as { tokens?: number | TokenBreakdown } | undefined;
if (!tokenPart || typeof tokenPart.tokens === 'undefined') {
return 0;
}
if (typeof tokenPart.tokens === 'number') {
return tokenPart.tokens;
}
return sumTokenBreakdown(tokenPart.tokens);
};