@tyvm/knowhow 0.0.105 → 0.0.106
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.
- package/CONFIG.md +8 -5
- package/package.json +3 -2
- package/scripts/check-model-pricing.ts +509 -0
- package/scripts/compare-openrouter-coverage.ts +576 -0
- package/src/agents/base/base.ts +127 -2
- package/src/agents/tools/execCommand.ts +4 -0
- package/src/agents/tools/executeScript/definition.ts +1 -1
- package/src/agents/tools/index.ts +0 -1
- package/src/agents/tools/list.ts +3 -43
- package/src/agents/tools/writeFile.ts +1 -1
- package/src/auth/browserLogin.ts +9 -4
- package/src/chat/modules/RemoteSyncModule.ts +3 -0
- package/src/cli.ts +31 -1
- package/src/clients/cerebras.ts +10 -0
- package/src/clients/contextLimits.ts +7 -2
- package/src/clients/copilot.ts +23 -0
- package/src/clients/deepseek.ts +16 -0
- package/src/clients/fireworks.ts +15 -0
- package/src/clients/gemini.ts +45 -2
- package/src/clients/github.ts +16 -0
- package/src/clients/groq.ts +15 -0
- package/src/clients/http.ts +190 -6
- package/src/clients/index.ts +116 -4
- package/src/clients/llama.ts +16 -0
- package/src/clients/mistral.ts +16 -0
- package/src/clients/nvidia.ts +16 -0
- package/src/clients/openai.ts +41 -11
- package/src/clients/openrouter.ts +17 -0
- package/src/clients/pricing/anthropic.ts +105 -78
- package/src/clients/pricing/cerebras.ts +11 -0
- package/src/clients/pricing/copilot.ts +60 -0
- package/src/clients/pricing/deepseek.ts +15 -0
- package/src/clients/pricing/fireworks.ts +32 -0
- package/src/clients/pricing/github.ts +69 -0
- package/src/clients/pricing/google.ts +245 -206
- package/src/clients/pricing/groq.ts +56 -0
- package/src/clients/pricing/index.ts +42 -5
- package/src/clients/pricing/llama.ts +18 -0
- package/src/clients/pricing/mistral.ts +34 -0
- package/src/clients/pricing/models.ts +7 -236
- package/src/clients/pricing/nvidia.ts +102 -0
- package/src/clients/pricing/openai.ts +347 -171
- package/src/clients/pricing/openrouter.ts +36 -0
- package/src/clients/pricing/types.ts +83 -2
- package/src/clients/pricing/xai.ts +121 -65
- package/src/clients/types.ts +4 -0
- package/src/clients/xai.ts +150 -0
- package/src/fileSync.ts +8 -2
- package/src/login.ts +11 -3
- package/src/services/AgentSyncFs.ts +36 -12
- package/src/services/KnowhowClient.ts +11 -0
- package/src/services/LazyToolsService.ts +6 -0
- package/src/services/S3.ts +0 -7
- package/src/services/modules/index.ts +11 -2
- package/src/types.ts +56 -279
- package/src/worker.ts +174 -0
- package/tests/clients/pricing.test.ts +37 -0
- package/tests/manual/clients/completions.json +838 -226
- package/tests/manual/clients/completions.test.ts +46 -31
- package/ts_build/package.json +3 -2
- package/ts_build/src/agents/base/base.d.ts +17 -1
- package/ts_build/src/agents/base/base.js +82 -1
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/tools/execCommand.js +3 -0
- package/ts_build/src/agents/tools/execCommand.js.map +1 -1
- package/ts_build/src/agents/tools/executeScript/definition.js +1 -1
- package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -1
- package/ts_build/src/agents/tools/index.d.ts +0 -1
- package/ts_build/src/agents/tools/index.js +0 -1
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +3 -38
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/visionTool.d.ts +1 -1
- package/ts_build/src/agents/tools/writeFile.js +1 -1
- package/ts_build/src/agents/tools/writeFile.js.map +1 -1
- package/ts_build/src/ai.d.ts +1 -1
- package/ts_build/src/auth/browserLogin.d.ts +2 -1
- package/ts_build/src/auth/browserLogin.js +10 -3
- package/ts_build/src/auth/browserLogin.js.map +1 -1
- package/ts_build/src/chat/modules/RemoteSyncModule.js +1 -0
- package/ts_build/src/chat/modules/RemoteSyncModule.js.map +1 -1
- package/ts_build/src/cli.js +19 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +1 -82
- package/ts_build/src/clients/cerebras.d.ts +4 -0
- package/ts_build/src/clients/cerebras.js +14 -0
- package/ts_build/src/clients/cerebras.js.map +1 -0
- package/ts_build/src/clients/contextLimits.js +7 -2
- package/ts_build/src/clients/contextLimits.js.map +1 -1
- package/ts_build/src/clients/copilot.d.ts +4 -0
- package/ts_build/src/clients/copilot.js +15 -0
- package/ts_build/src/clients/copilot.js.map +1 -0
- package/ts_build/src/clients/deepseek.d.ts +4 -0
- package/ts_build/src/clients/deepseek.js +15 -0
- package/ts_build/src/clients/deepseek.js.map +1 -0
- package/ts_build/src/clients/fireworks.d.ts +4 -0
- package/ts_build/src/clients/fireworks.js +15 -0
- package/ts_build/src/clients/fireworks.js.map +1 -0
- package/ts_build/src/clients/gemini.d.ts +1 -0
- package/ts_build/src/clients/gemini.js +28 -1
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/github.d.ts +4 -0
- package/ts_build/src/clients/github.js +15 -0
- package/ts_build/src/clients/github.js.map +1 -0
- package/ts_build/src/clients/groq.d.ts +4 -0
- package/ts_build/src/clients/groq.js +15 -0
- package/ts_build/src/clients/groq.js.map +1 -0
- package/ts_build/src/clients/http.d.ts +22 -1
- package/ts_build/src/clients/http.js +132 -7
- package/ts_build/src/clients/http.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +14 -0
- package/ts_build/src/clients/index.js +94 -4
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/llama.d.ts +4 -0
- package/ts_build/src/clients/llama.js +15 -0
- package/ts_build/src/clients/llama.js.map +1 -0
- package/ts_build/src/clients/mistral.d.ts +4 -0
- package/ts_build/src/clients/mistral.js +15 -0
- package/ts_build/src/clients/mistral.js.map +1 -0
- package/ts_build/src/clients/nvidia.d.ts +4 -0
- package/ts_build/src/clients/nvidia.js +15 -0
- package/ts_build/src/clients/nvidia.js.map +1 -0
- package/ts_build/src/clients/openai.d.ts +4 -206
- package/ts_build/src/clients/openai.js +27 -9
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/openrouter.d.ts +4 -0
- package/ts_build/src/clients/openrouter.js +15 -0
- package/ts_build/src/clients/openrouter.js.map +1 -0
- package/ts_build/src/clients/pricing/anthropic.d.ts +26 -78
- package/ts_build/src/clients/pricing/anthropic.js +75 -78
- package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
- package/ts_build/src/clients/pricing/cerebras.d.ts +4 -0
- package/ts_build/src/clients/pricing/cerebras.js +11 -0
- package/ts_build/src/clients/pricing/cerebras.js.map +1 -0
- package/ts_build/src/clients/pricing/copilot.d.ts +5 -0
- package/ts_build/src/clients/pricing/copilot.js +35 -0
- package/ts_build/src/clients/pricing/copilot.js.map +1 -0
- package/ts_build/src/clients/pricing/deepseek.d.ts +5 -0
- package/ts_build/src/clients/pricing/deepseek.js +10 -0
- package/ts_build/src/clients/pricing/deepseek.js.map +1 -0
- package/ts_build/src/clients/pricing/fireworks.d.ts +5 -0
- package/ts_build/src/clients/pricing/fireworks.js +21 -0
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -0
- package/ts_build/src/clients/pricing/github.d.ts +4 -0
- package/ts_build/src/clients/pricing/github.js +58 -0
- package/ts_build/src/clients/pricing/github.js.map +1 -0
- package/ts_build/src/clients/pricing/google.d.ts +59 -6
- package/ts_build/src/clients/pricing/google.js +214 -167
- package/ts_build/src/clients/pricing/google.js.map +1 -1
- package/ts_build/src/clients/pricing/groq.d.ts +5 -0
- package/ts_build/src/clients/pricing/groq.js +41 -0
- package/ts_build/src/clients/pricing/groq.js.map +1 -0
- package/ts_build/src/clients/pricing/index.d.ts +16 -5
- package/ts_build/src/clients/pricing/index.js +62 -7
- package/ts_build/src/clients/pricing/index.js.map +1 -1
- package/ts_build/src/clients/pricing/llama.d.ts +4 -0
- package/ts_build/src/clients/pricing/llama.js +14 -0
- package/ts_build/src/clients/pricing/llama.js.map +1 -0
- package/ts_build/src/clients/pricing/mistral.d.ts +5 -0
- package/ts_build/src/clients/pricing/mistral.js +23 -0
- package/ts_build/src/clients/pricing/mistral.js.map +1 -0
- package/ts_build/src/clients/pricing/models.d.ts +5 -4
- package/ts_build/src/clients/pricing/models.js +8 -162
- package/ts_build/src/clients/pricing/models.js.map +1 -1
- package/ts_build/src/clients/pricing/nvidia.d.ts +8 -0
- package/ts_build/src/clients/pricing/nvidia.js +96 -0
- package/ts_build/src/clients/pricing/nvidia.js.map +1 -0
- package/ts_build/src/clients/pricing/openai.d.ts +86 -197
- package/ts_build/src/clients/pricing/openai.js +294 -168
- package/ts_build/src/clients/pricing/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/openrouter.d.ts +4 -0
- package/ts_build/src/clients/pricing/openrouter.js +29 -0
- package/ts_build/src/clients/pricing/openrouter.js.map +1 -0
- package/ts_build/src/clients/pricing/types.d.ts +27 -2
- package/ts_build/src/clients/pricing/types.js +46 -0
- package/ts_build/src/clients/pricing/types.js.map +1 -1
- package/ts_build/src/clients/pricing/xai.d.ts +37 -57
- package/ts_build/src/clients/pricing/xai.js +92 -59
- package/ts_build/src/clients/pricing/xai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +1 -0
- package/ts_build/src/clients/xai.d.ts +2 -62
- package/ts_build/src/clients/xai.js +121 -0
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/fileSync.js +7 -2
- package/ts_build/src/fileSync.js.map +1 -1
- package/ts_build/src/login.js +8 -2
- package/ts_build/src/login.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.js +1 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +1 -0
- package/ts_build/src/services/KnowhowClient.js +7 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/LazyToolsService.d.ts +1 -0
- package/ts_build/src/services/LazyToolsService.js +3 -0
- package/ts_build/src/services/LazyToolsService.js.map +1 -1
- package/ts_build/src/services/S3.js +0 -7
- package/ts_build/src/services/S3.js.map +1 -1
- package/ts_build/src/services/modules/index.js +41 -1
- package/ts_build/src/services/modules/index.js.map +1 -1
- package/ts_build/src/types.d.ts +163 -124
- package/ts_build/src/types.js +33 -213
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/worker.d.ts +4 -0
- package/ts_build/src/worker.js +140 -0
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/clients/pricing.test.js +21 -0
- package/ts_build/tests/clients/pricing.test.js.map +1 -1
- package/ts_build/tests/manual/clients/completions.test.js +27 -24
- package/ts_build/tests/manual/clients/completions.test.js.map +1 -1
|
@@ -4,7 +4,8 @@ export type ModelType =
|
|
|
4
4
|
| "image"
|
|
5
5
|
| "audio"
|
|
6
6
|
| "video"
|
|
7
|
-
| "transaction"
|
|
7
|
+
| "transaction"
|
|
8
|
+
| "live";
|
|
8
9
|
|
|
9
10
|
export interface ModelPricing {
|
|
10
11
|
input?: number;
|
|
@@ -17,13 +18,93 @@ export interface ModelPricing {
|
|
|
17
18
|
input_gt_200k?: number;
|
|
18
19
|
output_gt_200k?: number;
|
|
19
20
|
image_generation?: number;
|
|
21
|
+
image_generation_per_1m_tokens?: number;
|
|
20
22
|
video_generation?: number;
|
|
23
|
+
output_image_per_1m_tokens?: number;
|
|
24
|
+
// Optional metadata — when set on a pricing entry, the catalog picks them up automatically
|
|
25
|
+
deprecated?: boolean;
|
|
26
|
+
deprecationDate?: string;
|
|
27
|
+
limitedAvailability?: boolean;
|
|
28
|
+
replacedBy?: string;
|
|
29
|
+
/** Supported reasoning effort levels for this model (ordered low→high). If set, effort will be clamped to these values. */
|
|
30
|
+
reasoningLevels?: string[];
|
|
31
|
+
/** If true, this model must be called via the Responses API (/v1/responses) instead of /v1/chat/completions */
|
|
32
|
+
useResponsesApi?: boolean;
|
|
21
33
|
}
|
|
22
34
|
|
|
23
35
|
export interface ModelCatalogEntry {
|
|
24
36
|
id: string;
|
|
25
37
|
provider: string;
|
|
26
38
|
type: ModelType;
|
|
27
|
-
displayName: string;
|
|
28
39
|
pricing: ModelPricing;
|
|
40
|
+
deprecated?: boolean;
|
|
41
|
+
deprecationDate?: string;
|
|
42
|
+
/** Model exists but is not generally available (e.g. Live API only, limited access, or returns empty responses) */
|
|
43
|
+
limitedAvailability?: boolean;
|
|
44
|
+
/** Recommended replacement model ID when this model is deprecated */
|
|
45
|
+
replacedBy?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── Bulk catalog helpers ─────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
export interface DeprecationOptions {
|
|
51
|
+
deprecated?: boolean;
|
|
52
|
+
deprecationDate?: string;
|
|
53
|
+
limitedAvailability?: boolean;
|
|
54
|
+
replacedBy?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function makeEntries(
|
|
58
|
+
type: ModelType,
|
|
59
|
+
ids: string[],
|
|
60
|
+
provider: string,
|
|
61
|
+
pricing: Record<string, ModelPricing>,
|
|
62
|
+
deprecation?: DeprecationOptions
|
|
63
|
+
): ModelCatalogEntry[] {
|
|
64
|
+
return ids.map((id) => {
|
|
65
|
+
const p = pricing[id] ?? {};
|
|
66
|
+
// Explicit dep options take precedence; fall back to metadata embedded in the pricing entry
|
|
67
|
+
const deprecated = deprecation?.deprecated ?? p.deprecated;
|
|
68
|
+
const deprecationDate = deprecation?.deprecationDate ?? p.deprecationDate;
|
|
69
|
+
const limitedAvailability = deprecation?.limitedAvailability ?? p.limitedAvailability;
|
|
70
|
+
const replacedBy = deprecation?.replacedBy ?? p.replacedBy;
|
|
71
|
+
// Strip metadata fields before storing as pricing
|
|
72
|
+
const { deprecated: _d, deprecationDate: _dd, limitedAvailability: _la, replacedBy: _rb, reasoningLevels: _rl, useResponsesApi: _ura, ...pricingOnly } = p;
|
|
73
|
+
return {
|
|
74
|
+
id,
|
|
75
|
+
provider,
|
|
76
|
+
type,
|
|
77
|
+
pricing: { input: 0, output: 0, ...pricingOnly },
|
|
78
|
+
deprecated,
|
|
79
|
+
deprecationDate,
|
|
80
|
+
limitedAvailability,
|
|
81
|
+
replacedBy,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const completions = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("completion", ids, provider, pricing, dep);
|
|
87
|
+
export const embeddings = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("embedding", ids, provider, pricing, dep);
|
|
88
|
+
export const images = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("image", ids, provider, pricing, dep);
|
|
89
|
+
export const videos = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("video", ids, provider, pricing, dep);
|
|
90
|
+
export const audios = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("audio", ids, provider, pricing, dep);
|
|
91
|
+
export const transactions = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("transaction", ids, provider, pricing, dep);
|
|
92
|
+
export const liveApi = (ids: string[], provider: string, pricing: Record<string, ModelPricing>, dep?: DeprecationOptions) => makeEntries("live", ids, provider, pricing, dep);
|
|
93
|
+
|
|
94
|
+
// ─── Single-entry helpers (for deprecated/special cases) ─────────────────────
|
|
95
|
+
|
|
96
|
+
export function completion(
|
|
97
|
+
id: string,
|
|
98
|
+
provider: string,
|
|
99
|
+
pricing: Partial<ModelPricing> = {},
|
|
100
|
+
deprecation?: DeprecationOptions
|
|
101
|
+
): ModelCatalogEntry {
|
|
102
|
+
return {
|
|
103
|
+
id, provider, type: "completion",
|
|
104
|
+
pricing: { input: 0, output: 0, ...pricing },
|
|
105
|
+
deprecated: deprecation?.deprecated,
|
|
106
|
+
deprecationDate: deprecation?.deprecationDate,
|
|
107
|
+
limitedAvailability: deprecation?.limitedAvailability,
|
|
108
|
+
replacedBy: deprecation?.replacedBy,
|
|
109
|
+
};
|
|
29
110
|
}
|
|
@@ -1,74 +1,130 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* xAI model IDs, pricing, and catalog.
|
|
3
|
+
* Single source of truth for all xAI/Grok models.
|
|
4
|
+
*/
|
|
5
|
+
import { completions, images, videos, ModelCatalogEntry, ModelPricing } from "./types";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
// ─── Model IDs ────────────────────────────────────────────────────────────────
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
9
|
+
export const XaiModels = {
|
|
10
|
+
Grok_4_20_Reasoning: "grok-4.20-0309-reasoning",
|
|
11
|
+
Grok_4_20_NonReasoning: "grok-4.20-0309-non-reasoning",
|
|
12
|
+
Grok_4_20_MultiAgent: "grok-4.20-multi-agent-0309",
|
|
13
|
+
Grok4_1_Fast_Reasoning: "grok-4-1-fast-reasoning",
|
|
14
|
+
Grok4_1_Fast_NonReasoning: "grok-4-1-fast-non-reasoning",
|
|
15
|
+
GrokCodeFast: "grok-code-fast-1",
|
|
16
|
+
Grok4: "grok-4-0709",
|
|
17
|
+
Grok3Beta: "grok-3-beta",
|
|
18
|
+
Grok3MiniBeta: "grok-3-mini-beta",
|
|
19
|
+
Grok3FastBeta: "grok-3-fast-beta",
|
|
20
|
+
Grok3MiniFastBeta: "grok-3-mini-fast-beta",
|
|
21
|
+
// Deprecated alias IDs used by models.dev (latest aliases and older beta names)
|
|
22
|
+
Grok2Latest: "grok-2-latest",
|
|
23
|
+
Grok2VisionLatest: "grok-2-vision-latest",
|
|
24
|
+
Grok3Latest: "grok-3-latest",
|
|
25
|
+
Grok3FastLatest: "grok-3-fast-latest",
|
|
26
|
+
Grok3MiniLatest: "grok-3-mini-latest",
|
|
27
|
+
Grok3MiniFastLatest: "grok-3-mini-fast-latest",
|
|
28
|
+
GrokBeta: "grok-beta",
|
|
29
|
+
GrokVisionBeta: "grok-vision-beta",
|
|
30
|
+
// grok-4-1-fast variants (aliases for grok-4-1-fast-reasoning/non-reasoning)
|
|
31
|
+
Grok4_1_Fast: "grok-4-1-fast",
|
|
32
|
+
Grok4Fast: "grok-4-fast",
|
|
33
|
+
Grok4FastNonReasoning: "grok-4-fast-non-reasoning",
|
|
34
|
+
Grok21212: "grok-2-1212",
|
|
35
|
+
Grok2Vision1212: "grok-2-vision-1212",
|
|
36
|
+
GrokImagineImage: "grok-imagine-image",
|
|
37
|
+
GrokImagineVideo: "grok-imagine-video",
|
|
38
|
+
Grok2Image1212: "grok-2-image-1212",
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
// ─── Modality arrays ──────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export const XaiTextModels: string[] = [
|
|
44
|
+
XaiModels.Grok_4_20_Reasoning, XaiModels.Grok_4_20_NonReasoning,
|
|
45
|
+
XaiModels.Grok_4_20_MultiAgent,
|
|
46
|
+
XaiModels.Grok4_1_Fast_Reasoning, XaiModels.Grok4_1_Fast_NonReasoning,
|
|
47
|
+
XaiModels.GrokCodeFast, XaiModels.Grok4,
|
|
48
|
+
XaiModels.Grok3Beta, XaiModels.Grok3MiniBeta, XaiModels.Grok3FastBeta, XaiModels.Grok3MiniFastBeta,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
// Models that require the Responses API (/v1/responses) instead of /v1/chat/completions
|
|
52
|
+
// The xAI reasoning variants and multi-agent model use the Responses API
|
|
53
|
+
export const XaiResponsesOnlyModels: string[] = [
|
|
54
|
+
XaiModels.Grok_4_20_Reasoning,
|
|
55
|
+
XaiModels.Grok_4_20_NonReasoning,
|
|
56
|
+
XaiModels.Grok_4_20_MultiAgent,
|
|
57
|
+
XaiModels.Grok4_1_Fast_Reasoning,
|
|
58
|
+
XaiModels.Grok4_1_Fast_NonReasoning,
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// Models that support the reasoning_effort parameter
|
|
62
|
+
// grok-3-mini variants support reasoning_effort; grok-3-beta, grok-4 etc. do NOT
|
|
63
|
+
export const XaiReasoningModels: string[] = [
|
|
64
|
+
XaiModels.Grok_4_20_MultiAgent,
|
|
65
|
+
XaiModels.Grok3MiniBeta,
|
|
66
|
+
XaiModels.Grok3MiniFastBeta,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
// Deprecated xAI models — "Model not found" (400) when called
|
|
70
|
+
export const XaiDeprecatedTextModels: string[] = [
|
|
71
|
+
XaiModels.Grok21212, XaiModels.Grok2Vision1212,
|
|
72
|
+
// Alias IDs from models.dev that map to deprecated/versioned models
|
|
73
|
+
XaiModels.Grok2Latest, XaiModels.Grok2VisionLatest,
|
|
74
|
+
XaiModels.Grok3Latest, XaiModels.Grok3FastLatest,
|
|
75
|
+
XaiModels.Grok3MiniLatest, XaiModels.Grok3MiniFastLatest,
|
|
76
|
+
XaiModels.GrokBeta, XaiModels.GrokVisionBeta,
|
|
77
|
+
XaiModels.Grok4_1_Fast, XaiModels.Grok4Fast, XaiModels.Grok4FastNonReasoning,
|
|
78
|
+
];
|
|
79
|
+
export const XaiImageModels: string[] = [XaiModels.GrokImagineImage, XaiModels.Grok2Image1212];
|
|
80
|
+
export const XaiVideoModels: string[] = [XaiModels.GrokImagineVideo];
|
|
81
|
+
|
|
82
|
+
// ─── Pricing (USD per 1M tokens / per-image / per-second) ────────────────────
|
|
83
|
+
|
|
84
|
+
export const XaiTextPricing: Record<string, ModelPricing> = {
|
|
85
|
+
[XaiModels.Grok_4_20_Reasoning]: { input: 2.0, cache_hit: 0.20, output: 6.0, useResponsesApi: true },
|
|
86
|
+
[XaiModels.Grok_4_20_NonReasoning]: { input: 2.0, cache_hit: 0.20, output: 6.0, useResponsesApi: true },
|
|
87
|
+
[XaiModels.Grok_4_20_MultiAgent]: { input: 2.0, cache_hit: 0.20, output: 6.0, reasoningLevels: ["low", "medium", "high", "xhigh"], useResponsesApi: true },
|
|
88
|
+
[XaiModels.Grok4_1_Fast_Reasoning]: { input: 0.2, cache_hit: 0.05, output: 0.5, useResponsesApi: true },
|
|
89
|
+
[XaiModels.Grok4_1_Fast_NonReasoning]: { input: 0.2, cache_hit: 0.05, output: 0.5, useResponsesApi: true },
|
|
90
|
+
[XaiModels.GrokCodeFast]: { input: 0.2, cache_hit: 0.02, output: 1.5 },
|
|
91
|
+
[XaiModels.Grok4]: { input: 3.0, output: 15.0 },
|
|
92
|
+
[XaiModels.Grok3Beta]: { input: 3.0, output: 15.0 },
|
|
93
|
+
[XaiModels.Grok3MiniBeta]: { input: 0.3, output: 0.5 },
|
|
94
|
+
[XaiModels.Grok3FastBeta]: { input: 5.0, output: 25.0 },
|
|
95
|
+
[XaiModels.Grok3MiniFastBeta]: { input: 0.6, output: 4.0 },
|
|
96
|
+
[XaiModels.Grok21212]: { input: 2.0, output: 10.0, deprecated: true },
|
|
97
|
+
[XaiModels.Grok2Vision1212]: { input: 2.0, output: 10.0, deprecated: true },
|
|
98
|
+
// Deprecated alias IDs (models.dev uses these; they map to versioned/beta models above)
|
|
99
|
+
[XaiModels.Grok2Latest]: { input: 2.0, output: 10.0, deprecated: true },
|
|
100
|
+
[XaiModels.Grok2VisionLatest]: { input: 2.0, output: 10.0, deprecated: true },
|
|
101
|
+
[XaiModels.Grok3Latest]: { input: 3.0, output: 15.0, deprecated: true },
|
|
102
|
+
[XaiModels.Grok3FastLatest]: { input: 5.0, output: 25.0, deprecated: true },
|
|
103
|
+
[XaiModels.Grok3MiniLatest]: { input: 0.3, output: 0.5, deprecated: true },
|
|
104
|
+
[XaiModels.Grok3MiniFastLatest]: { input: 0.6, output: 4.0, deprecated: true },
|
|
105
|
+
[XaiModels.GrokBeta]: { input: 5.0, output: 15.0, deprecated: true },
|
|
106
|
+
[XaiModels.GrokVisionBeta]: { input: 5.0, output: 15.0, deprecated: true },
|
|
107
|
+
// grok-4-1-fast / grok-4-fast aliases — deprecated in favor of versioned reasoning/non-reasoning variants
|
|
108
|
+
[XaiModels.Grok4_1_Fast]: { input: 0.2, output: 0.5, deprecated: true },
|
|
109
|
+
[XaiModels.Grok4Fast]: { input: 0.2, output: 0.5, deprecated: true },
|
|
110
|
+
[XaiModels.Grok4FastNonReasoning]:{ input: 0.2, output: 0.5, deprecated: true },
|
|
60
111
|
};
|
|
61
112
|
|
|
62
|
-
// Image generation pricing: per image
|
|
63
|
-
// Based on https://docs.x.ai/developers/models
|
|
64
113
|
export const XaiImagePricing: Record<string, ModelPricing> = {
|
|
65
|
-
"grok-imagine-image-pro":
|
|
66
|
-
|
|
67
|
-
|
|
114
|
+
"grok-imagine-image-pro": { image_generation: 0.07 },
|
|
115
|
+
[XaiModels.GrokImagineImage]: { image_generation: 0.02 },
|
|
116
|
+
[XaiModels.Grok2Image1212]: { image_generation: 0.07 },
|
|
68
117
|
};
|
|
69
118
|
|
|
70
|
-
// Video generation pricing: $0.05 per second
|
|
71
|
-
// Based on https://docs.x.ai/developers/models
|
|
72
119
|
export const XaiVideoPricing: Record<string, ModelPricing> = {
|
|
73
|
-
|
|
120
|
+
[XaiModels.GrokImagineVideo]: { video_generation: 0.05 },
|
|
74
121
|
};
|
|
122
|
+
|
|
123
|
+
// ─── Catalog ──────────────────────────────────────────────────────────────────
|
|
124
|
+
// Metadata (deprecated, useResponsesApi) is read directly from XaiTextPricing entries.
|
|
125
|
+
|
|
126
|
+
export const XAI_MODEL_CATALOG: ModelCatalogEntry[] = [
|
|
127
|
+
...completions([...XaiTextModels, ...XaiDeprecatedTextModels], "xai", XaiTextPricing),
|
|
128
|
+
...images(XaiImageModels, "xai", XaiImagePricing),
|
|
129
|
+
...videos(XaiVideoModels, "xai", XaiVideoPricing),
|
|
130
|
+
];
|
package/src/clients/types.ts
CHANGED
|
@@ -57,6 +57,10 @@ export interface CompletionOptions {
|
|
|
57
57
|
tools?: Tool[];
|
|
58
58
|
tool_choice?: "auto" | "none";
|
|
59
59
|
max_tokens?: number;
|
|
60
|
+
/** Reasoning effort level for models that support it.
|
|
61
|
+
* Maps to: OpenAI reasoning_effort, xAI reasoning.effort, Gemini thinkingLevel/thinkingBudget, Anthropic thinking budget.
|
|
62
|
+
* "low" = minimal thinking, "medium" = balanced, "high" = maximum reasoning */
|
|
63
|
+
reasoning_effort?: "low" | "medium" | "high";
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
export interface CompletionResponse {
|
package/src/clients/xai.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
|
|
30
30
|
import { Models, XaiImageModels, XaiVideoModels } from "../types";
|
|
31
31
|
import { ModelModality } from "./types";
|
|
32
|
+
import { XaiReasoningModels, XaiResponsesOnlyModels } from "./pricing/xai";
|
|
32
33
|
|
|
33
34
|
export class GenericXAIClient implements GenericClient {
|
|
34
35
|
private client: OpenAI;
|
|
@@ -54,6 +55,11 @@ export class GenericXAIClient implements GenericClient {
|
|
|
54
55
|
async createChatCompletion(
|
|
55
56
|
options: CompletionOptions
|
|
56
57
|
): Promise<CompletionResponse> {
|
|
58
|
+
// Route to Responses API for models that require it
|
|
59
|
+
if (XaiResponsesOnlyModels.includes(options.model)) {
|
|
60
|
+
return this.createChatResponse(options);
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
const xaiMessages = options.messages.map((msg) => {
|
|
58
64
|
if (msg.role === "tool") {
|
|
59
65
|
return {
|
|
@@ -70,6 +76,10 @@ export class GenericXAIClient implements GenericClient {
|
|
|
70
76
|
model: options.model,
|
|
71
77
|
messages: xaiMessages,
|
|
72
78
|
max_tokens: options.max_tokens,
|
|
79
|
+
...(XaiReasoningModels.includes(options.model) && options.reasoning_effort && {
|
|
80
|
+
// grok-3-mini models support reasoning_effort: "low" | "medium" | "high"
|
|
81
|
+
reasoning_effort: options.reasoning_effort,
|
|
82
|
+
}),
|
|
73
83
|
...(options.tools && {
|
|
74
84
|
tools: options.tools,
|
|
75
85
|
tool_choice: "auto",
|
|
@@ -94,6 +104,146 @@ export class GenericXAIClient implements GenericClient {
|
|
|
94
104
|
};
|
|
95
105
|
}
|
|
96
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Creates a completion using the xAI Responses API (/v1/responses).
|
|
109
|
+
* Used for grok-4.20 reasoning/non-reasoning and multi-agent models.
|
|
110
|
+
* Translates Chat Completions message format to Responses API format.
|
|
111
|
+
*/
|
|
112
|
+
async createChatResponse(
|
|
113
|
+
options: CompletionOptions
|
|
114
|
+
): Promise<CompletionResponse> {
|
|
115
|
+
const apiKey = this.apiKey || process.env.XAI_API_KEY;
|
|
116
|
+
if (!apiKey) {
|
|
117
|
+
throw new Error("XAI API key not set");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Extract system messages as instructions
|
|
121
|
+
const systemMessages = options.messages.filter((m) => m.role === "system");
|
|
122
|
+
const nonSystemMessages = options.messages.filter((m) => m.role !== "system");
|
|
123
|
+
const instructions = systemMessages
|
|
124
|
+
.map((m) => (typeof m.content === "string" ? m.content : ""))
|
|
125
|
+
.join("\n")
|
|
126
|
+
.trim() || undefined;
|
|
127
|
+
|
|
128
|
+
// Convert chat messages to Responses API input items
|
|
129
|
+
const input: any[] = nonSystemMessages.map((msg) => {
|
|
130
|
+
if (msg.role === "tool") {
|
|
131
|
+
return {
|
|
132
|
+
type: "function_call_output",
|
|
133
|
+
call_id: msg.tool_call_id,
|
|
134
|
+
output: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (msg.role === "assistant" && msg.tool_calls?.length) {
|
|
138
|
+
return msg.tool_calls.map((tc) => ({
|
|
139
|
+
type: "function_call",
|
|
140
|
+
id: tc.id.startsWith("fc") ? tc.id : `fc_${tc.id}`,
|
|
141
|
+
call_id: tc.id,
|
|
142
|
+
name: tc.function.name,
|
|
143
|
+
arguments: tc.function.arguments,
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
role: msg.role,
|
|
148
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
149
|
+
};
|
|
150
|
+
}).flat();
|
|
151
|
+
|
|
152
|
+
// Convert tool definitions to Responses API format
|
|
153
|
+
const tools = options.tools?.map((tool) => ({
|
|
154
|
+
type: "function" as const,
|
|
155
|
+
name: tool.function.name,
|
|
156
|
+
description: tool.function.description,
|
|
157
|
+
parameters: tool.function.parameters as Record<string, unknown>,
|
|
158
|
+
strict: false,
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
// Resolve reasoning effort, clamping to supported levels if defined in pricing
|
|
162
|
+
const pricing = XaiTextPricing[options.model];
|
|
163
|
+
const supportedLevels = pricing?.reasoningLevels;
|
|
164
|
+
let reasoningEffort: string | undefined = options.reasoning_effort;
|
|
165
|
+
if (supportedLevels?.length) {
|
|
166
|
+
if (!reasoningEffort || !supportedLevels.includes(reasoningEffort)) {
|
|
167
|
+
reasoningEffort = supportedLevels[0];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const body: any = {
|
|
172
|
+
model: options.model,
|
|
173
|
+
input,
|
|
174
|
+
...(instructions && { instructions }),
|
|
175
|
+
...(options.max_tokens && { max_output_tokens: Math.max(options.max_tokens, 16_000) }),
|
|
176
|
+
...(reasoningEffort && { reasoning: { effort: reasoningEffort } }),
|
|
177
|
+
...(tools?.length && { tools, tool_choice: "auto" }),
|
|
178
|
+
store: false,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const response = await fetch("https://api.x.ai/v1/responses", {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: {
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
Authorization: `Bearer ${apiKey}`,
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify(body),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
const errorText = await response.text();
|
|
192
|
+
throw new Error(`XAI Responses API error: ${response.status} ${errorText}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const data = await response.json();
|
|
196
|
+
|
|
197
|
+
// Map usage
|
|
198
|
+
const usage = data.usage
|
|
199
|
+
? {
|
|
200
|
+
prompt_tokens: data.usage.input_tokens,
|
|
201
|
+
completion_tokens: data.usage.output_tokens,
|
|
202
|
+
total_tokens: data.usage.input_tokens + data.usage.output_tokens,
|
|
203
|
+
}
|
|
204
|
+
: undefined;
|
|
205
|
+
|
|
206
|
+
const usdCost = usage ? this.calculateCost(options.model, usage) : undefined;
|
|
207
|
+
|
|
208
|
+
// Collect text content and tool calls from output items
|
|
209
|
+
let textContent: string | null = null;
|
|
210
|
+
const toolCalls: any[] = [];
|
|
211
|
+
|
|
212
|
+
for (const item of data.output ?? []) {
|
|
213
|
+
if (item.type === "message") {
|
|
214
|
+
for (const part of item.content ?? []) {
|
|
215
|
+
if (part.type === "output_text") {
|
|
216
|
+
textContent = (textContent ?? "") + part.text;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else if (item.type === "function_call") {
|
|
220
|
+
toolCalls.push({
|
|
221
|
+
id: item.call_id,
|
|
222
|
+
type: "function",
|
|
223
|
+
function: {
|
|
224
|
+
name: item.name,
|
|
225
|
+
arguments: item.arguments,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
choices: [
|
|
233
|
+
{
|
|
234
|
+
message: {
|
|
235
|
+
role: "assistant",
|
|
236
|
+
content: textContent,
|
|
237
|
+
...(toolCalls.length > 0 && { tool_calls: toolCalls }),
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
model: options.model,
|
|
242
|
+
usage,
|
|
243
|
+
usd_cost: usdCost,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
97
247
|
pricesPerMillion() {
|
|
98
248
|
return XaiTextPricing;
|
|
99
249
|
}
|
package/src/fileSync.ts
CHANGED
|
@@ -265,8 +265,14 @@ export async function uploadDirectory(
|
|
|
265
265
|
for (const relFile of localFiles) {
|
|
266
266
|
const localFilePath = localDir + relFile;
|
|
267
267
|
const remoteFilePath = remoteDir + relFile;
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
try {
|
|
269
|
+
await uploadFile(client, s3Service, remoteFilePath, localFilePath, dryRun);
|
|
270
|
+
count++;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(
|
|
273
|
+
` ❌ Failed to upload ${localFilePath}, skipping: ${error.message}`
|
|
274
|
+
);
|
|
275
|
+
}
|
|
270
276
|
}
|
|
271
277
|
return count;
|
|
272
278
|
}
|
package/src/login.ts
CHANGED
|
@@ -29,7 +29,10 @@ export async function login(jwtFlag?: boolean): Promise<void> {
|
|
|
29
29
|
// Use browser login as default method
|
|
30
30
|
console.log("Starting browser-based authentication...");
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
// Pass existing orgId from config (if any) so the browser pre-selects the right org
|
|
33
|
+
const existingConfig = await getConfig();
|
|
34
|
+
const existingOrgId = existingConfig?.orgId;
|
|
35
|
+
const browserLoginService = new BrowserLoginService(undefined, existingOrgId);
|
|
33
36
|
await browserLoginService.login();
|
|
34
37
|
console.log("Successfully authenticated via browser!");
|
|
35
38
|
} catch (error) {
|
|
@@ -43,6 +46,7 @@ export async function login(jwtFlag?: boolean): Promise<void> {
|
|
|
43
46
|
try {
|
|
44
47
|
const storedJwt = await loadJwt();
|
|
45
48
|
const { user, currentOrg } = await checkJwt(storedJwt);
|
|
49
|
+
const orgId = currentOrg?.organizationId;
|
|
46
50
|
|
|
47
51
|
console.log(
|
|
48
52
|
`Current user: ${user.email}, \nOrganization: ${currentOrg?.organization?.name} - ${currentOrg?.organization?.id}`
|
|
@@ -61,9 +65,13 @@ export async function login(jwtFlag?: boolean): Promise<void> {
|
|
|
61
65
|
config.modelProviders.push({
|
|
62
66
|
provider: "knowhow",
|
|
63
67
|
});
|
|
64
|
-
|
|
65
|
-
await updateConfig(config);
|
|
66
68
|
}
|
|
69
|
+
// Save orgId to config so sync:remote and other features use the correct org
|
|
70
|
+
if (orgId) {
|
|
71
|
+
config.orgId = orgId;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await updateConfig(config);
|
|
67
75
|
} catch (error) {
|
|
68
76
|
if (http.isHttpError(error) && error.response) {
|
|
69
77
|
const errData = await error.response.json().catch(() => ({ message: "Unknown error" }));
|
|
@@ -121,7 +121,11 @@ export class AgentSyncFs {
|
|
|
121
121
|
/**
|
|
122
122
|
* Update metadata file with current agent state
|
|
123
123
|
*/
|
|
124
|
-
private async updateMetadata(
|
|
124
|
+
private async updateMetadata(
|
|
125
|
+
agent: BaseAgent,
|
|
126
|
+
inProgress: boolean,
|
|
127
|
+
result?: string
|
|
128
|
+
): Promise<void> {
|
|
125
129
|
if (!this.taskPath) return;
|
|
126
130
|
|
|
127
131
|
try {
|
|
@@ -137,7 +141,8 @@ export class AgentSyncFs {
|
|
|
137
141
|
|
|
138
142
|
metadata.threads = agent.getThreads();
|
|
139
143
|
metadata.totalCostUsd = agent.getTotalCostUsd();
|
|
140
|
-
|
|
144
|
+
metadata.tokenUsage = agent.getTokenUsage();
|
|
145
|
+
metadata.agentName = agent.name;
|
|
141
146
|
metadata.inProgress = inProgress;
|
|
142
147
|
metadata.lastUpdate = new Date().toISOString();
|
|
143
148
|
|
|
@@ -147,7 +152,11 @@ export class AgentSyncFs {
|
|
|
147
152
|
await this.writeStatus("completed");
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
await fs.writeFile(
|
|
155
|
+
await fs.writeFile(
|
|
156
|
+
metadataPath,
|
|
157
|
+
JSON.stringify(metadata, null, 2),
|
|
158
|
+
"utf8"
|
|
159
|
+
);
|
|
151
160
|
} catch (error) {
|
|
152
161
|
console.error(`❌ Failed to update metadata:`, error);
|
|
153
162
|
}
|
|
@@ -206,7 +215,9 @@ export class AgentSyncFs {
|
|
|
206
215
|
// Check for new input/messages
|
|
207
216
|
const input = await this.readInput();
|
|
208
217
|
if (input && input !== this.lastInputContent && input.trim() !== "") {
|
|
209
|
-
console.log(
|
|
218
|
+
console.log(
|
|
219
|
+
`📬 New message received via filesystem for task ${this.taskId}`
|
|
220
|
+
);
|
|
210
221
|
this.lastInputContent = input;
|
|
211
222
|
|
|
212
223
|
agent.addPendingUserMessage({
|
|
@@ -234,7 +245,7 @@ export class AgentSyncFs {
|
|
|
234
245
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
235
246
|
|
|
236
247
|
const status = await this.readStatus();
|
|
237
|
-
|
|
248
|
+
|
|
238
249
|
if (status === "killed") {
|
|
239
250
|
console.log(`🛑 Agent task ${this.taskId} killed while paused`);
|
|
240
251
|
await agent.kill();
|
|
@@ -314,7 +325,10 @@ export class AgentSyncFs {
|
|
|
314
325
|
console.error(`❌ Error during threadUpdate sync:`, error);
|
|
315
326
|
}
|
|
316
327
|
};
|
|
317
|
-
agent.agentEvents.on(
|
|
328
|
+
agent.agentEvents.on(
|
|
329
|
+
agent.eventTypes.threadUpdate,
|
|
330
|
+
this.threadUpdateHandler
|
|
331
|
+
);
|
|
318
332
|
|
|
319
333
|
// Listen to completion event to finalize task (store reference for cleanup)
|
|
320
334
|
this.doneHandler = (result: string) => {
|
|
@@ -323,7 +337,9 @@ export class AgentSyncFs {
|
|
|
323
337
|
return;
|
|
324
338
|
}
|
|
325
339
|
|
|
326
|
-
console.log(
|
|
340
|
+
console.log(
|
|
341
|
+
`🎯 [AgentSyncFs] Done event received for task: ${this.taskId}`
|
|
342
|
+
);
|
|
327
343
|
|
|
328
344
|
// Store finalization promise so callers can await it (same pattern as AgentSyncKnowhowWeb)
|
|
329
345
|
this.finalizationPromise = (async () => {
|
|
@@ -332,7 +348,9 @@ export class AgentSyncFs {
|
|
|
332
348
|
// This prevents the race where a pending "inProgress: true" metadata write
|
|
333
349
|
// overwrites the finalization write.
|
|
334
350
|
if (this.pendingThreadUpdatePromise) {
|
|
335
|
-
console.log(
|
|
351
|
+
console.log(
|
|
352
|
+
`⏳ [AgentSyncFs] Awaiting pending thread update before finalizing...`
|
|
353
|
+
);
|
|
336
354
|
await this.pendingThreadUpdatePromise.catch(() => {
|
|
337
355
|
// Ignore errors in pending update — we still want to finalize
|
|
338
356
|
});
|
|
@@ -379,7 +397,7 @@ export class AgentSyncFs {
|
|
|
379
397
|
private static async cleanupOldTasks(): Promise<void> {
|
|
380
398
|
try {
|
|
381
399
|
const agentsPath = AgentSyncFs.sharedBasePath;
|
|
382
|
-
|
|
400
|
+
|
|
383
401
|
// Check if directory exists
|
|
384
402
|
try {
|
|
385
403
|
await fs.access(agentsPath);
|
|
@@ -396,7 +414,7 @@ export class AgentSyncFs {
|
|
|
396
414
|
if (!entry.isDirectory()) continue;
|
|
397
415
|
|
|
398
416
|
const taskPath = path.join(agentsPath, entry.name);
|
|
399
|
-
|
|
417
|
+
|
|
400
418
|
try {
|
|
401
419
|
const stats = await fs.stat(taskPath);
|
|
402
420
|
const age = now - stats.mtimeMs;
|
|
@@ -449,11 +467,17 @@ export class AgentSyncFs {
|
|
|
449
467
|
// Remove old event listeners from the agent before resetting
|
|
450
468
|
if (this.agent) {
|
|
451
469
|
if (this.threadUpdateHandler) {
|
|
452
|
-
this.agent.agentEvents.removeListener(
|
|
470
|
+
this.agent.agentEvents.removeListener(
|
|
471
|
+
this.agent.eventTypes.threadUpdate,
|
|
472
|
+
this.threadUpdateHandler
|
|
473
|
+
);
|
|
453
474
|
this.threadUpdateHandler = undefined;
|
|
454
475
|
}
|
|
455
476
|
if (this.doneHandler) {
|
|
456
|
-
this.agent.agentEvents.removeListener(
|
|
477
|
+
this.agent.agentEvents.removeListener(
|
|
478
|
+
this.agent.eventTypes.done,
|
|
479
|
+
this.doneHandler
|
|
480
|
+
);
|
|
457
481
|
this.doneHandler = undefined;
|
|
458
482
|
}
|
|
459
483
|
this.agent = undefined;
|