btca-server 1.0.962 → 2.0.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 (43) hide show
  1. package/package.json +3 -3
  2. package/src/agent/agent.test.ts +31 -24
  3. package/src/agent/index.ts +8 -2
  4. package/src/agent/loop.ts +303 -346
  5. package/src/agent/service.ts +252 -233
  6. package/src/agent/types.ts +2 -2
  7. package/src/collections/index.ts +2 -1
  8. package/src/collections/service.ts +352 -345
  9. package/src/config/config.test.ts +3 -1
  10. package/src/config/index.ts +615 -727
  11. package/src/config/remote.ts +214 -369
  12. package/src/context/index.ts +6 -12
  13. package/src/context/transaction.ts +23 -30
  14. package/src/effect/errors.ts +45 -0
  15. package/src/effect/layers.ts +26 -0
  16. package/src/effect/runtime.ts +19 -0
  17. package/src/effect/services.ts +154 -0
  18. package/src/index.ts +291 -369
  19. package/src/metrics/index.ts +46 -46
  20. package/src/pricing/models-dev.ts +104 -106
  21. package/src/providers/auth.ts +159 -200
  22. package/src/providers/index.ts +19 -2
  23. package/src/providers/model.ts +115 -135
  24. package/src/providers/openai.ts +3 -3
  25. package/src/resources/impls/git.ts +123 -146
  26. package/src/resources/impls/npm.test.ts +16 -5
  27. package/src/resources/impls/npm.ts +66 -75
  28. package/src/resources/index.ts +6 -1
  29. package/src/resources/schema.ts +7 -6
  30. package/src/resources/service.test.ts +13 -12
  31. package/src/resources/service.ts +153 -112
  32. package/src/stream/index.ts +1 -1
  33. package/src/stream/service.test.ts +5 -5
  34. package/src/stream/service.ts +282 -293
  35. package/src/tools/glob.ts +126 -141
  36. package/src/tools/grep.ts +205 -210
  37. package/src/tools/index.ts +8 -4
  38. package/src/tools/list.ts +118 -140
  39. package/src/tools/read.ts +209 -235
  40. package/src/tools/virtual-sandbox.ts +91 -83
  41. package/src/validation/index.ts +18 -22
  42. package/src/vfs/virtual-fs.test.ts +37 -25
  43. package/src/vfs/virtual-fs.ts +218 -216
@@ -1,33 +1,26 @@
1
- import { Result } from 'better-result';
1
+ import { metricsError, metricsErrorInfo, metricsInfo } from '../metrics/index.ts';
2
+ import { requireContext } from './index.ts';
2
3
 
3
- import { Metrics } from '../metrics/index.ts';
4
- import { Context } from './index.ts';
4
+ export const runTransaction = async <T>(name: string, fn: () => Promise<T>): Promise<T> => {
5
+ const store = requireContext();
6
+ const depth = store.txDepth;
7
+ store.txDepth = depth + 1;
5
8
 
6
- export namespace Transaction {
7
- export const run = async <T>(name: string, fn: () => Promise<T>): Promise<T> => {
8
- const store = Context.require();
9
- const depth = store.txDepth;
10
- store.txDepth = depth + 1;
11
-
12
- const start = performance.now();
13
- Metrics.info('tx.start', { name, depth });
14
- const result = await Result.tryPromise(fn);
15
- store.txDepth = depth;
16
-
17
- return result.match({
18
- ok: (value) => {
19
- Metrics.info('tx.commit', { name, depth, ms: Math.round(performance.now() - start) });
20
- return value;
21
- },
22
- err: (cause) => {
23
- Metrics.error('tx.rollback', {
24
- name,
25
- depth,
26
- ms: Math.round(performance.now() - start),
27
- error: Metrics.errorInfo(cause)
28
- });
29
- throw cause;
30
- }
9
+ const start = performance.now();
10
+ metricsInfo('tx.start', { name, depth });
11
+ try {
12
+ const value = await fn();
13
+ metricsInfo('tx.commit', { name, depth, ms: Math.round(performance.now() - start) });
14
+ return value;
15
+ } catch (cause) {
16
+ metricsError('tx.rollback', {
17
+ name,
18
+ depth,
19
+ ms: Math.round(performance.now() - start),
20
+ error: metricsErrorInfo(cause)
31
21
  });
32
- };
33
- }
22
+ throw cause;
23
+ } finally {
24
+ store.txDepth = depth;
25
+ }
26
+ };
@@ -0,0 +1,45 @@
1
+ import { Data } from 'effect';
2
+ import { getErrorHint, getErrorMessage, getErrorTag } from '../errors.ts';
3
+
4
+ export class ServerError extends Data.TaggedError('ServerError')<{
5
+ readonly message: string;
6
+ readonly hint?: string;
7
+ readonly cause?: unknown;
8
+ }> {}
9
+
10
+ export interface HttpErrorPayload {
11
+ readonly error: string;
12
+ readonly tag: string;
13
+ readonly hint?: string;
14
+ readonly status: number;
15
+ }
16
+
17
+ const getHttpStatusFromErrorTag = (tag: string) => {
18
+ if (
19
+ tag === 'RequestError' ||
20
+ tag === 'CollectionError' ||
21
+ tag === 'ResourceError' ||
22
+ tag === 'ConfigError' ||
23
+ tag === 'InvalidProviderError' ||
24
+ tag === 'InvalidModelError' ||
25
+ tag === 'ProviderNotAuthenticatedError' ||
26
+ tag === 'ProviderAuthTypeError' ||
27
+ tag === 'ProviderNotFoundError' ||
28
+ tag === 'ProviderNotConnectedError' ||
29
+ tag === 'ProviderOptionsError'
30
+ ) {
31
+ return 400;
32
+ }
33
+ if (tag === 'RouteNotFound') return 404;
34
+ return 500;
35
+ };
36
+
37
+ export const toHttpErrorPayload = (error: unknown): HttpErrorPayload => {
38
+ const tag = getErrorTag(error);
39
+ return {
40
+ error: getErrorMessage(error),
41
+ tag,
42
+ hint: getErrorHint(error),
43
+ status: getHttpStatusFromErrorTag(tag)
44
+ };
45
+ };
@@ -0,0 +1,26 @@
1
+ import { Layer, ServiceMap, pipe } from 'effect';
2
+ import type { AgentService as AgentServiceShape } from '../agent/service.ts';
3
+ import type { CollectionsService as CollectionsServiceShape } from '../collections/service.ts';
4
+ import type { ConfigService as ConfigServiceShape } from '../config/index.ts';
5
+ import { AgentService, CollectionsService, ConfigService } from './services.ts';
6
+
7
+ export type ServerLayerDependencies = {
8
+ config: ConfigServiceShape;
9
+ collections: CollectionsServiceShape;
10
+ agent: AgentServiceShape;
11
+ };
12
+
13
+ export const makeServerLayer = (dependencies: ServerLayerDependencies) =>
14
+ Layer.mergeAll(
15
+ Layer.succeed(ConfigService, dependencies.config),
16
+ Layer.succeed(CollectionsService, dependencies.collections),
17
+ Layer.succeed(AgentService, dependencies.agent)
18
+ );
19
+
20
+ export const makeServerServiceMap = (dependencies: ServerLayerDependencies) =>
21
+ pipe(
22
+ ServiceMap.empty(),
23
+ ServiceMap.add(ConfigService, dependencies.config),
24
+ ServiceMap.add(CollectionsService, dependencies.collections),
25
+ ServiceMap.add(AgentService, dependencies.agent)
26
+ );
@@ -0,0 +1,19 @@
1
+ import { Effect, Exit, ManagedRuntime } from 'effect';
2
+ import { makeServerLayer, makeServerServiceMap, type ServerLayerDependencies } from './layers.ts';
3
+
4
+ export interface ServerRuntime {
5
+ runPromise: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>;
6
+ runPromiseExit: <A, E>(effect: Effect.Effect<A, E>) => Promise<Exit.Exit<A, E>>;
7
+ services: () => Promise<ReturnType<typeof makeServerServiceMap>>;
8
+ dispose: () => Promise<void>;
9
+ }
10
+
11
+ export const createServerRuntime = (dependencies: ServerLayerDependencies): ServerRuntime => {
12
+ const runtime = ManagedRuntime.make(makeServerLayer(dependencies));
13
+ return {
14
+ runPromise: (effect) => runtime.runPromise(effect),
15
+ runPromiseExit: (effect) => runtime.runPromiseExit(effect),
16
+ services: () => runtime.services(),
17
+ dispose: () => runtime.dispose()
18
+ };
19
+ };
@@ -0,0 +1,154 @@
1
+ import { Effect, ServiceMap } from 'effect';
2
+ import type { AgentService as AgentServiceShape } from '../agent/service.ts';
3
+ import type { CollectionsService as CollectionsServiceShape } from '../collections/service.ts';
4
+ import { getCollectionKey } from '../collections/types.ts';
5
+ import type { ConfigService as ConfigServiceShape } from '../config/index.ts';
6
+ import type { ResourceDefinition } from '../resources/schema.ts';
7
+
8
+ export class ConfigService extends ServiceMap.Service<ConfigService, ConfigServiceShape>()(
9
+ 'btca-server/effect/ConfigService'
10
+ ) {}
11
+
12
+ export class CollectionsService extends ServiceMap.Service<
13
+ CollectionsService,
14
+ CollectionsServiceShape
15
+ >()('btca-server/effect/CollectionsService') {}
16
+
17
+ export class AgentService extends ServiceMap.Service<AgentService, AgentServiceShape>()(
18
+ 'btca-server/effect/AgentService'
19
+ ) {}
20
+
21
+ const configService = Effect.service(ConfigService);
22
+ const collectionsService = Effect.service(CollectionsService);
23
+ const agentService = Effect.service(AgentService);
24
+
25
+ export type ConfigSnapshot = {
26
+ provider: string;
27
+ model: string;
28
+ providerTimeoutMs: number | null;
29
+ maxSteps: number;
30
+ resourcesDirectory: string;
31
+ resourceCount: number;
32
+ };
33
+
34
+ export type ResourcesSnapshot = {
35
+ resources: Array<{
36
+ name: string;
37
+ type: 'git' | 'local' | 'npm';
38
+ url?: string;
39
+ branch?: string;
40
+ path?: string;
41
+ package?: string;
42
+ version?: string | null;
43
+ searchPath?: string | null;
44
+ searchPaths?: string[] | null;
45
+ specialNotes?: string | null;
46
+ }>;
47
+ };
48
+
49
+ export const getConfigSnapshot: Effect.Effect<ConfigSnapshot, never, ConfigService> = Effect.map(
50
+ configService,
51
+ (config) => ({
52
+ provider: config.provider,
53
+ model: config.model,
54
+ providerTimeoutMs: config.providerTimeoutMs ?? null,
55
+ maxSteps: config.maxSteps,
56
+ resourcesDirectory: config.resourcesDirectory,
57
+ resourceCount: config.resources.length
58
+ })
59
+ );
60
+
61
+ export const getResourcesSnapshot: Effect.Effect<ResourcesSnapshot, never, ConfigService> =
62
+ Effect.map(configService, (config) => ({
63
+ resources: config.resources.map((resource) => {
64
+ if (resource.type === 'git') {
65
+ return {
66
+ name: resource.name,
67
+ type: resource.type,
68
+ url: resource.url,
69
+ branch: resource.branch,
70
+ searchPath: resource.searchPath ?? null,
71
+ searchPaths: resource.searchPaths ?? null,
72
+ specialNotes: resource.specialNotes ?? null
73
+ };
74
+ }
75
+ if (resource.type === 'local') {
76
+ return {
77
+ name: resource.name,
78
+ type: resource.type,
79
+ path: resource.path,
80
+ specialNotes: resource.specialNotes ?? null
81
+ };
82
+ }
83
+ return {
84
+ name: resource.name,
85
+ type: resource.type,
86
+ package: resource.package,
87
+ version: resource.version ?? null,
88
+ specialNotes: resource.specialNotes ?? null
89
+ };
90
+ })
91
+ }));
92
+
93
+ export const getDefaultResourceNames: Effect.Effect<string[], never, ConfigService> = Effect.map(
94
+ configService,
95
+ (config) => config.resources.map((resource) => resource.name)
96
+ );
97
+
98
+ export const reloadConfig: Effect.Effect<void, unknown, ConfigService> = Effect.flatMap(
99
+ configService,
100
+ (config) => config.reloadEffect()
101
+ );
102
+
103
+ export const listProviders: Effect.Effect<
104
+ Awaited<ReturnType<AgentServiceShape['listProviders']>>,
105
+ unknown,
106
+ AgentService
107
+ > = Effect.flatMap(agentService, (agent) => agent.listProvidersEffect());
108
+
109
+ export const loadCollection = (args: {
110
+ resourceNames: readonly string[];
111
+ quiet?: boolean;
112
+ }): Effect.Effect<
113
+ Awaited<ReturnType<CollectionsServiceShape['load']>>,
114
+ unknown,
115
+ CollectionsService
116
+ > => Effect.flatMap(collectionsService, (collections) => collections.loadEffect(args));
117
+
118
+ export const askQuestion = (args: {
119
+ collection: Awaited<ReturnType<CollectionsServiceShape['load']>>;
120
+ question: string;
121
+ }): Effect.Effect<Awaited<ReturnType<AgentServiceShape['ask']>>, unknown, AgentService> =>
122
+ Effect.flatMap(agentService, (agent) => agent.askEffect(args));
123
+
124
+ export const askQuestionStream = (args: {
125
+ collection: Awaited<ReturnType<CollectionsServiceShape['load']>>;
126
+ question: string;
127
+ }): Effect.Effect<Awaited<ReturnType<AgentServiceShape['askStream']>>, unknown, AgentService> =>
128
+ Effect.flatMap(agentService, (agent) => agent.askStreamEffect(args));
129
+
130
+ export const updateModelConfig = (args: {
131
+ provider: string;
132
+ model: string;
133
+ providerOptions?: Parameters<ConfigServiceShape['updateModel']>[2];
134
+ }): Effect.Effect<Awaited<ReturnType<ConfigServiceShape['updateModel']>>, unknown, ConfigService> =>
135
+ Effect.flatMap(configService, (config) =>
136
+ config.updateModelEffect(args.provider, args.model, args.providerOptions)
137
+ );
138
+
139
+ export const addConfigResource = (
140
+ resource: ResourceDefinition
141
+ ): Effect.Effect<ResourceDefinition, unknown, ConfigService> =>
142
+ Effect.flatMap(configService, (config) => config.addResourceEffect(resource));
143
+
144
+ export const removeConfigResource = (name: string): Effect.Effect<void, unknown, ConfigService> =>
145
+ Effect.flatMap(configService, (config) => config.removeResourceEffect(name));
146
+
147
+ export const clearConfigResources: Effect.Effect<
148
+ Awaited<ReturnType<ConfigServiceShape['clearResources']>>,
149
+ unknown,
150
+ ConfigService
151
+ > = Effect.flatMap(configService, (config) => config.clearResourcesEffect());
152
+
153
+ export const loadedResourceCollectionKey = (resourceNames: readonly string[]) =>
154
+ getCollectionKey(resourceNames);