@foxden-app/foxclaw 0.7.3 → 0.10.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 (58) hide show
  1. package/.env.example +13 -0
  2. package/CHANGELOG.md +109 -0
  3. package/README.md +21 -1
  4. package/README_EN.md +21 -1
  5. package/dist/antigravity/adapter.d.ts +17 -0
  6. package/dist/antigravity/adapter.js +256 -0
  7. package/dist/antigravity/auth.d.ts +121 -0
  8. package/dist/antigravity/auth.js +896 -0
  9. package/dist/antigravity/client.d.ts +32 -0
  10. package/dist/antigravity/client.js +238 -0
  11. package/dist/antigravity/controller.d.ts +96 -0
  12. package/dist/antigravity/controller.js +1922 -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 +101 -0
  16. package/dist/antigravity/events.js +114 -0
  17. package/dist/antigravity/runtime.d.ts +47 -0
  18. package/dist/antigravity/runtime.js +82 -0
  19. package/dist/channels/telegram/telegram_channel_adapter.d.ts +10 -3
  20. package/dist/channels/telegram/telegram_channel_adapter.js +2 -1
  21. package/dist/channels/weixin/weixin_channel_adapter.d.ts +5 -1
  22. package/dist/codex_app/adapter.d.ts +20 -0
  23. package/dist/codex_app/adapter.js +213 -0
  24. package/dist/codex_app/client.d.ts +1 -1
  25. package/dist/codex_app/client.js +12 -0
  26. package/dist/config.d.ts +12 -0
  27. package/dist/config.js +42 -0
  28. package/dist/controller/controller.d.ts +8 -5
  29. package/dist/controller/controller.js +38 -8
  30. package/dist/core/attachments.d.ts +11 -0
  31. package/dist/core/attachments.js +56 -0
  32. package/dist/core/engine_spi.d.ts +76 -0
  33. package/dist/core/engine_spi.js +1 -0
  34. package/dist/core/orchestrator.d.ts +101 -0
  35. package/dist/core/orchestrator.js +1404 -0
  36. package/dist/core/stream_preview.d.ts +29 -0
  37. package/dist/core/stream_preview.js +146 -0
  38. package/dist/core/turn_queue.d.ts +19 -0
  39. package/dist/core/turn_queue.js +41 -0
  40. package/dist/i18n.d.ts +6 -0
  41. package/dist/i18n.js +49 -0
  42. package/dist/main.js +146 -12
  43. package/dist/opencode/adapter.d.ts +11 -0
  44. package/dist/opencode/adapter.js +168 -0
  45. package/dist/store/database.d.ts +41 -0
  46. package/dist/store/database.js +242 -43
  47. package/dist/store/token_usage.d.ts +23 -0
  48. package/dist/store/token_usage.js +61 -0
  49. package/dist/telegram/api.js +30 -1
  50. package/dist/telegram/gateway.d.ts +4 -0
  51. package/dist/telegram/gateway.js +11 -0
  52. package/dist/types.d.ts +1 -0
  53. package/dist/update.d.ts +6 -0
  54. package/dist/update.js +78 -6
  55. package/dist/voice/tts.js +4 -2
  56. 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
  57. 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
  58. package/package.json +2 -2
@@ -0,0 +1,168 @@
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
+ }
68
+ if (cancelled)
69
+ return;
70
+ let accumulatedResponse = '';
71
+ const onEvent = (ev) => {
72
+ if ('sessionId' in ev && ev.sessionId === sessionId) {
73
+ if (ev.kind === 'text') {
74
+ if (ev.delta) {
75
+ accumulatedResponse += ev.delta;
76
+ emitter.emit('delta', ev.delta);
77
+ }
78
+ }
79
+ else if (ev.kind === 'tool') {
80
+ emitter.emit('tool', {
81
+ name: ev.tool,
82
+ args: undefined,
83
+ output: undefined,
84
+ status: ev.status === 'running' ? 'running' : ev.status === 'error' ? 'failed' : 'completed',
85
+ });
86
+ }
87
+ else if (ev.kind === 'idle') {
88
+ cleanup();
89
+ finalResult = {
90
+ kind: 'result',
91
+ status: 'SUCCESS',
92
+ response: accumulatedResponse,
93
+ conversationId: sessionId,
94
+ };
95
+ emitter.emit('result', finalResult);
96
+ resolveWaiters(finalResult);
97
+ }
98
+ else if (ev.kind === 'error') {
99
+ cleanup();
100
+ turnError = new Error(ev.message);
101
+ finalResult = {
102
+ kind: 'result',
103
+ status: 'ERROR',
104
+ response: ev.message,
105
+ conversationId: sessionId,
106
+ };
107
+ emitter.emit('result', finalResult);
108
+ emitter.emit('error', turnError);
109
+ resolveWaiters(finalResult);
110
+ }
111
+ }
112
+ };
113
+ const cleanup = () => {
114
+ this.client.off('event', onEvent);
115
+ };
116
+ this.client.on('event', onEvent);
117
+ let modelParam;
118
+ const targetModel = request.model || this.defaultModel;
119
+ if (targetModel && targetModel.includes('/')) {
120
+ const [pId, ...mRest] = targetModel.split('/');
121
+ modelParam = { providerID: pId, modelID: mRest.join('/') };
122
+ }
123
+ const promptRes = await sdk.session.promptAsync({
124
+ sessionID: sessionId,
125
+ directory: request.cwd,
126
+ parts: [{ type: 'text', text: promptText }],
127
+ ...(modelParam ? { model: modelParam } : {}),
128
+ });
129
+ if (promptRes.error) {
130
+ cleanup();
131
+ turnError = new Error(formatSdkError(promptRes.error));
132
+ emitter.emit('error', turnError);
133
+ resolveWaiters(null);
134
+ }
135
+ else {
136
+ this.client.watchSessionUntilIdle(sessionId, request.cwd);
137
+ }
138
+ }
139
+ catch (err) {
140
+ turnError = err instanceof Error ? err : new Error(String(err));
141
+ emitter.emit('error', turnError);
142
+ resolveWaiters(null);
143
+ }
144
+ };
145
+ void run();
146
+ return {
147
+ turnId: sessionId ?? undefined,
148
+ cancel: () => {
149
+ cancelled = true;
150
+ if (sessionId) {
151
+ void this.client.getClient().session.abort({ sessionID: sessionId }).catch(() => { });
152
+ }
153
+ },
154
+ waitForResult: async () => {
155
+ if (finalResult)
156
+ return finalResult;
157
+ if (turnError)
158
+ return null;
159
+ return new Promise((resolve) => {
160
+ resultWaiters.push(resolve);
161
+ });
162
+ },
163
+ on: (event, listener) => {
164
+ emitter.on(event, listener);
165
+ },
166
+ };
167
+ }
168
+ }
@@ -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;
@@ -62,6 +63,12 @@ export declare class BridgeStore {
62
63
  setBinding(chatId: string, threadId: string, cwd: string | null): void;
63
64
  listBindings(): ThreadBinding[];
64
65
  clearBinding(chatId: string): void;
66
+ getWatchedThread(chatId: string): string | null;
67
+ setWatchedThread(chatId: string, threadId: string | null): void;
68
+ listWatchedThreads(): Array<{
69
+ scopeId: string;
70
+ threadId: string;
71
+ }>;
65
72
  getChatSettings(chatId: string): ChatSessionSettings | null;
66
73
  setChatSettings(chatId: string, model: string | null, reasoningEffort: ReasoningEffortValue | null, locale?: AppLocale | null): void;
67
74
  setChatLocale(chatId: string, locale: AppLocale): void;
@@ -69,6 +76,30 @@ export declare class BridgeStore {
69
76
  setChatCollaborationMode(chatId: string, collaborationMode: CollaborationModeValue | null): void;
70
77
  setChatServiceTier(chatId: string, serviceTier: string | null): void;
71
78
  setChatActiveTurnMessageMode(chatId: string, activeTurnMessageMode: ActiveTurnMessageMode | null): void;
79
+ getActiveBackend(chatId: string): string | null;
80
+ setActiveBackend(chatId: string, backendId: string | null): void;
81
+ getScopeBackendBinding(scopeId: string, backendId: string): {
82
+ threadId: string;
83
+ cwd: string | null;
84
+ model?: string | null;
85
+ reasoningEffort?: string | null;
86
+ activeTurnMessageMode?: string | null;
87
+ serviceTier?: string | null;
88
+ accessPreset?: string | null;
89
+ } | null;
90
+ setScopeBackendBinding(scopeId: string, backendId: string, threadId: string, cwd: string | null, settings?: {
91
+ model?: string | null;
92
+ reasoningEffort?: string | null;
93
+ activeTurnMessageMode?: string | null;
94
+ serviceTier?: string | null;
95
+ accessPreset?: string | null;
96
+ } | null): void;
97
+ listScopeBackendBindings(scopeId: string): Array<{
98
+ backendId: string;
99
+ threadId: string;
100
+ cwd: string | null;
101
+ updatedAt: number;
102
+ }>;
72
103
  findChatIdByThreadId(threadId: string): string | null;
73
104
  findAllChatIdsByThreadId(threadId: string): string[];
74
105
  countBindings(): number;
@@ -141,5 +172,15 @@ export declare class BridgeStore {
141
172
  getCodexAuthPoolStats(): CodexAuthPoolStats;
142
173
  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
174
  listCodexAuthQuotaSnapshots(quotaIdentityIds: string[]): CodexAuthQuotaSnapshotRecord[];
175
+ recordTokenUsage(delta: TokenUsageDelta, backendId?: string): void;
176
+ setBackendCumulativeTokenUsage(usage: {
177
+ inputTokens: number;
178
+ outputTokens: number;
179
+ cachedTokens?: number;
180
+ totalTokens?: number;
181
+ turnsCount?: number;
182
+ }, backendId: string): void;
183
+ getCumulativeTokenUsage(backendId?: string): CumulativeTokenTotals;
184
+ getAllBackendTokenUsages(): CumulativeTokenUsageRecord[];
144
185
  private ensureColumn;
145
186
  }