@foxden-app/foxclaw 0.7.3 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.env.example +22 -8
  2. package/CHANGELOG.md +134 -0
  3. package/README.md +21 -1
  4. package/README_EN.md +21 -1
  5. package/dist/antigravity/adapter.d.ts +18 -0
  6. package/dist/antigravity/adapter.js +394 -0
  7. package/dist/antigravity/auth.d.ts +126 -0
  8. package/dist/antigravity/auth.js +933 -0
  9. package/dist/antigravity/client.d.ts +32 -0
  10. package/dist/antigravity/client.js +244 -0
  11. package/dist/antigravity/controller.d.ts +101 -0
  12. package/dist/antigravity/controller.js +2007 -0
  13. package/dist/antigravity/conversations.d.ts +29 -0
  14. package/dist/antigravity/conversations.js +289 -0
  15. package/dist/antigravity/events.d.ts +102 -0
  16. package/dist/antigravity/events.js +143 -0
  17. package/dist/antigravity/runtime.d.ts +49 -0
  18. package/dist/antigravity/runtime.js +82 -0
  19. package/dist/antigravity/subagents.d.ts +66 -0
  20. package/dist/antigravity/subagents.js +459 -0
  21. package/dist/channels/telegram/telegram_channel_adapter.d.ts +10 -3
  22. package/dist/channels/telegram/telegram_channel_adapter.js +2 -1
  23. package/dist/channels/weixin/weixin_channel_adapter.d.ts +5 -1
  24. package/dist/codex_app/adapter.d.ts +20 -0
  25. package/dist/codex_app/adapter.js +213 -0
  26. package/dist/codex_app/client.d.ts +1 -1
  27. package/dist/codex_app/client.js +12 -0
  28. package/dist/config.d.ts +14 -0
  29. package/dist/config.js +65 -16
  30. package/dist/controller/controller.d.ts +12 -5
  31. package/dist/controller/controller.js +109 -8
  32. package/dist/core/attachments.d.ts +11 -0
  33. package/dist/core/attachments.js +56 -0
  34. package/dist/core/engine_spi.d.ts +77 -0
  35. package/dist/core/engine_spi.js +1 -0
  36. package/dist/core/orchestrator.d.ts +106 -0
  37. package/dist/core/orchestrator.js +1556 -0
  38. package/dist/core/stream_preview.d.ts +29 -0
  39. package/dist/core/stream_preview.js +146 -0
  40. package/dist/core/turn_queue.d.ts +19 -0
  41. package/dist/core/turn_queue.js +41 -0
  42. package/dist/i18n.d.ts +6 -0
  43. package/dist/i18n.js +49 -0
  44. package/dist/main.js +375 -446
  45. package/dist/opencode/adapter.d.ts +11 -0
  46. package/dist/opencode/adapter.js +169 -0
  47. package/dist/opencode/controller.js +2 -2
  48. package/dist/store/database.d.ts +44 -0
  49. package/dist/store/database.js +275 -43
  50. package/dist/store/token_usage.d.ts +23 -0
  51. package/dist/store/token_usage.js +61 -0
  52. package/dist/telegram/api.d.ts +1 -0
  53. package/dist/telegram/api.js +80 -3
  54. package/dist/telegram/bot_home.js +5 -4
  55. package/dist/telegram/gateway.d.ts +4 -0
  56. package/dist/telegram/gateway.js +11 -0
  57. package/dist/types.d.ts +3 -0
  58. package/dist/update.d.ts +6 -0
  59. package/dist/update.js +95 -7
  60. package/dist/voice/tts.js +4 -2
  61. package/docs/devlogs//346/212/200/346/234/257/351/200/232/350/256/257/02./347/273/237/344/270/200/351/200/232/351/201/223/350/260/203/345/272/246/345/231/250/344/270/216/345/217/257/346/217/222/346/213/224/345/274/225/346/223/216SPI/357/274/232FoxClaw/344/273/216/345/215/225/344/275/223/350/265/260/345/220/221/345/244/232Agent/345/272/225/345/272/247.md +192 -0
  62. package/docs/devlogs//347/254/254/344/270/200/344/272/272/347/247/260/347/211/210/02./347/257/207/344/272/214_/345/275/223/344/270/200/344/270/252/346/241/245/346/216/245/345/231/250/345/274/200/345/247/213/345/255/246/344/274/232/345/220/254/346/207/202/346/211/200/346/234/211Agent.md +45 -0
  63. package/package.json +2 -2
@@ -0,0 +1,11 @@
1
+ import type { IEngineAdapter, EngineModel, EngineTurnRequest, EngineTurnExecution } from '../core/engine_spi.js';
2
+ import type { OpencodeAppClient } from './client.js';
3
+ export declare class OpencodeEngineAdapter implements IEngineAdapter {
4
+ private readonly client;
5
+ private readonly defaultModel?;
6
+ readonly id = "opencode";
7
+ readonly name = "OpenCode (SDK)";
8
+ constructor(client: OpencodeAppClient, defaultModel?: string | undefined);
9
+ listModels(): Promise<EngineModel[]>;
10
+ executeTurn(request: EngineTurnRequest): EngineTurnExecution;
11
+ }
@@ -0,0 +1,169 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { formatSdkError } from './client.js';
3
+ import { buildAttachmentPrompt } from '../telegram/media.js';
4
+ export class OpencodeEngineAdapter {
5
+ client;
6
+ defaultModel;
7
+ id = 'opencode';
8
+ name = 'OpenCode (SDK)';
9
+ constructor(client, defaultModel) {
10
+ this.client = client;
11
+ this.defaultModel = defaultModel;
12
+ }
13
+ async listModels() {
14
+ if (!this.client.isConnected())
15
+ return [];
16
+ try {
17
+ const response = await this.client.getClient().provider.list();
18
+ if (response.error || !response.data)
19
+ return [];
20
+ const { all, connected, default: defaultMap } = response.data;
21
+ const models = [];
22
+ for (const providerId of connected) {
23
+ const provider = all.find((p) => p.id === providerId);
24
+ if (!provider)
25
+ continue;
26
+ const defaultForProvider = defaultMap[providerId];
27
+ for (const m of Object.values(provider.models)) {
28
+ const modelFullId = `${providerId}/${m.id}`;
29
+ models.push({
30
+ id: modelFullId,
31
+ name: `${provider.name} - ${m.name}`,
32
+ isDefault: m.id === defaultForProvider,
33
+ });
34
+ }
35
+ }
36
+ return models;
37
+ }
38
+ catch {
39
+ return [];
40
+ }
41
+ }
42
+ executeTurn(request) {
43
+ const emitter = new EventEmitter();
44
+ let sessionId = request.threadId;
45
+ let cancelled = false;
46
+ let promptText = request.prompt;
47
+ if (request.stagedAttachments && request.stagedAttachments.length > 0 && !request.prompt.includes('Telegram attachments:')) {
48
+ promptText = buildAttachmentPrompt(request.prompt, request.stagedAttachments);
49
+ }
50
+ let finalResult = null;
51
+ let turnError = null;
52
+ const resultWaiters = [];
53
+ const resolveWaiters = (res) => {
54
+ while (resultWaiters.length > 0) {
55
+ resultWaiters.shift()(res);
56
+ }
57
+ };
58
+ const run = async () => {
59
+ try {
60
+ const sdk = this.client.getClient();
61
+ if (!sessionId) {
62
+ const createRes = await sdk.session.create({ directory: request.cwd });
63
+ if (createRes.error || !createRes.data) {
64
+ throw new Error(`Failed to create OpenCode session: ${formatSdkError(createRes.error)}`);
65
+ }
66
+ sessionId = createRes.data.id;
67
+ emitter.emit('conversation', sessionId);
68
+ }
69
+ if (cancelled)
70
+ return;
71
+ let accumulatedResponse = '';
72
+ const onEvent = (ev) => {
73
+ if ('sessionId' in ev && ev.sessionId === sessionId) {
74
+ if (ev.kind === 'text') {
75
+ if (ev.delta) {
76
+ accumulatedResponse += ev.delta;
77
+ emitter.emit('delta', ev.delta);
78
+ }
79
+ }
80
+ else if (ev.kind === 'tool') {
81
+ emitter.emit('tool', {
82
+ name: ev.tool,
83
+ args: undefined,
84
+ output: undefined,
85
+ status: ev.status === 'running' ? 'running' : ev.status === 'error' ? 'failed' : 'completed',
86
+ });
87
+ }
88
+ else if (ev.kind === 'idle') {
89
+ cleanup();
90
+ finalResult = {
91
+ kind: 'result',
92
+ status: 'SUCCESS',
93
+ response: accumulatedResponse,
94
+ conversationId: sessionId,
95
+ };
96
+ emitter.emit('result', finalResult);
97
+ resolveWaiters(finalResult);
98
+ }
99
+ else if (ev.kind === 'error') {
100
+ cleanup();
101
+ turnError = new Error(ev.message);
102
+ finalResult = {
103
+ kind: 'result',
104
+ status: 'ERROR',
105
+ response: ev.message,
106
+ conversationId: sessionId,
107
+ };
108
+ emitter.emit('result', finalResult);
109
+ emitter.emit('error', turnError);
110
+ resolveWaiters(finalResult);
111
+ }
112
+ }
113
+ };
114
+ const cleanup = () => {
115
+ this.client.off('event', onEvent);
116
+ };
117
+ this.client.on('event', onEvent);
118
+ let modelParam;
119
+ const targetModel = request.model || this.defaultModel;
120
+ if (targetModel && targetModel.includes('/')) {
121
+ const [pId, ...mRest] = targetModel.split('/');
122
+ modelParam = { providerID: pId, modelID: mRest.join('/') };
123
+ }
124
+ const promptRes = await sdk.session.promptAsync({
125
+ sessionID: sessionId,
126
+ directory: request.cwd,
127
+ parts: [{ type: 'text', text: promptText }],
128
+ ...(modelParam ? { model: modelParam } : {}),
129
+ });
130
+ if (promptRes.error) {
131
+ cleanup();
132
+ turnError = new Error(formatSdkError(promptRes.error));
133
+ emitter.emit('error', turnError);
134
+ resolveWaiters(null);
135
+ }
136
+ else {
137
+ this.client.watchSessionUntilIdle(sessionId, request.cwd);
138
+ }
139
+ }
140
+ catch (err) {
141
+ turnError = err instanceof Error ? err : new Error(String(err));
142
+ emitter.emit('error', turnError);
143
+ resolveWaiters(null);
144
+ }
145
+ };
146
+ void run();
147
+ return {
148
+ turnId: sessionId ?? undefined,
149
+ cancel: () => {
150
+ cancelled = true;
151
+ if (sessionId) {
152
+ void this.client.getClient().session.abort({ sessionID: sessionId }).catch(() => { });
153
+ }
154
+ },
155
+ waitForResult: async () => {
156
+ if (finalResult)
157
+ return finalResult;
158
+ if (turnError)
159
+ return null;
160
+ return new Promise((resolve) => {
161
+ resultWaiters.push(resolve);
162
+ });
163
+ },
164
+ on: (event, listener) => {
165
+ emitter.on(event, listener);
166
+ },
167
+ };
168
+ }
169
+ }
@@ -720,7 +720,7 @@ export class OpencodeBridgeCore {
720
720
  const model = index >= 0 ? models[index] : models.find((item) => `${item.providerId}/${item.modelId}` === normalized || item.modelId === normalized || storeModel(item.providerId, item.modelId) === normalized);
721
721
  if (!model)
722
722
  throw new Error(localize(locale, `未知模型:${raw}`, `Unknown model: ${raw}`));
723
- this.store.setChatSettings(scopeId, storeModel(model.providerId, model.modelId), null);
723
+ this.store.setChatModel(scopeId, storeModel(model.providerId, model.modelId));
724
724
  const prefs = readPrefs(this.store.getChatSettings(scopeId));
725
725
  if (prefs.variant && !model.variants.includes(prefs.variant))
726
726
  this.writePrefs(scopeId, { ...prefs, variant: null });
@@ -1792,7 +1792,7 @@ export class OpencodeBridgeCore {
1792
1792
  }
1793
1793
  else {
1794
1794
  const parsed = parseStoredModel(action.value);
1795
- this.store.setChatSettings(event.scopeId, action.value, null);
1795
+ this.store.setChatModel(event.scopeId, action.value);
1796
1796
  this.writePrefs(event.scopeId, { ...readPrefs(this.store.getChatSettings(event.scopeId)), variant: null });
1797
1797
  answer = `${parsed.providerId}/${parsed.modelId}`;
1798
1798
  }
@@ -1,4 +1,5 @@
1
1
  import type { AccessPresetValue, ActiveTurnMessageMode, AppLocale, CachedThread, ChatSessionSettings, CollaborationModeValue, GuidedPlanSessionRecord, GuidedPlanSessionState, PendingAttachmentBatchRecord, PendingAttachmentBatchStatus, PendingApprovalRecord, QueuedTurnInputRecord, QueuedTurnInputStatus, ReasoningEffortValue, ThreadBinding } from '../types.js';
2
+ import type { CumulativeTokenTotals, CumulativeTokenUsageRecord, TokenUsageDelta } from './token_usage.js';
2
3
  export interface ActiveTurnPreviewRecord {
3
4
  turnId: string;
4
5
  scopeId: string;
@@ -58,17 +59,50 @@ export declare class BridgeStore {
58
59
  scopeId: string;
59
60
  chatId: string;
60
61
  } | null;
62
+ migrateTelegramUnnamespacedScope(botId: string, chatId: string): void;
61
63
  getBinding(chatId: string): ThreadBinding | null;
62
64
  setBinding(chatId: string, threadId: string, cwd: string | null): void;
63
65
  listBindings(): ThreadBinding[];
64
66
  clearBinding(chatId: string): void;
67
+ getWatchedThread(chatId: string): string | null;
68
+ setWatchedThread(chatId: string, threadId: string | null): void;
69
+ listWatchedThreads(): Array<{
70
+ scopeId: string;
71
+ threadId: string;
72
+ }>;
65
73
  getChatSettings(chatId: string): ChatSessionSettings | null;
66
74
  setChatSettings(chatId: string, model: string | null, reasoningEffort: ReasoningEffortValue | null, locale?: AppLocale | null): void;
75
+ setChatModel(chatId: string, model: string | null): void;
76
+ setChatEffort(chatId: string, effort: ReasoningEffortValue | null): void;
67
77
  setChatLocale(chatId: string, locale: AppLocale): void;
68
78
  setChatAccessPreset(chatId: string, accessPreset: AccessPresetValue | null): void;
69
79
  setChatCollaborationMode(chatId: string, collaborationMode: CollaborationModeValue | null): void;
70
80
  setChatServiceTier(chatId: string, serviceTier: string | null): void;
71
81
  setChatActiveTurnMessageMode(chatId: string, activeTurnMessageMode: ActiveTurnMessageMode | null): void;
82
+ getActiveBackend(chatId: string): string | null;
83
+ setActiveBackend(chatId: string, backendId: string | null): void;
84
+ getScopeBackendBinding(scopeId: string, backendId: string): {
85
+ threadId: string;
86
+ cwd: string | null;
87
+ model?: string | null;
88
+ reasoningEffort?: string | null;
89
+ activeTurnMessageMode?: string | null;
90
+ serviceTier?: string | null;
91
+ accessPreset?: string | null;
92
+ } | null;
93
+ setScopeBackendBinding(scopeId: string, backendId: string, threadId: string, cwd: string | null, settings?: {
94
+ model?: string | null;
95
+ reasoningEffort?: string | null;
96
+ activeTurnMessageMode?: string | null;
97
+ serviceTier?: string | null;
98
+ accessPreset?: string | null;
99
+ } | null): void;
100
+ listScopeBackendBindings(scopeId: string): Array<{
101
+ backendId: string;
102
+ threadId: string;
103
+ cwd: string | null;
104
+ updatedAt: number;
105
+ }>;
72
106
  findChatIdByThreadId(threadId: string): string | null;
73
107
  findAllChatIdsByThreadId(threadId: string): string[];
74
108
  countBindings(): number;
@@ -141,5 +175,15 @@ export declare class BridgeStore {
141
175
  getCodexAuthPoolStats(): CodexAuthPoolStats;
142
176
  setCodexAuthQuotaSnapshot(runtimeId: string, candidateName: string, accountId: string, quotaIdentityId: string, snapshot: Pick<CodexAuthQuotaSnapshotRecord, 'capturedAtMs' | 'planType' | 'primaryWindowDurationMins' | 'primaryRemainingPercent' | 'secondaryWindowDurationMins' | 'secondaryRemainingPercent'> & Partial<Pick<CodexAuthQuotaSnapshotRecord, 'primaryResetsAt' | 'secondaryResetsAt'>>): void;
143
177
  listCodexAuthQuotaSnapshots(quotaIdentityIds: string[]): CodexAuthQuotaSnapshotRecord[];
178
+ recordTokenUsage(delta: TokenUsageDelta, backendId?: string): void;
179
+ setBackendCumulativeTokenUsage(usage: {
180
+ inputTokens: number;
181
+ outputTokens: number;
182
+ cachedTokens?: number;
183
+ totalTokens?: number;
184
+ turnsCount?: number;
185
+ }, backendId: string): void;
186
+ getCumulativeTokenUsage(backendId?: string): CumulativeTokenTotals;
187
+ getAllBackendTokenUsages(): CumulativeTokenUsageRecord[];
144
188
  private ensureColumn;
145
189
  }