btca-server 1.0.962 → 2.0.1

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,6 +1,8 @@
1
1
  import { createHash } from 'node:crypto';
2
2
 
3
- import { Config } from '../config/index.ts';
3
+ import { Effect } from 'effect';
4
+
5
+ import type { ConfigService as ConfigServiceShape } from '../config/index.ts';
4
6
  import { parseNpmReference, validateGitUrl } from '../validation/index.ts';
5
7
  import { CommonHints } from '../errors.ts';
6
8
 
@@ -33,135 +35,174 @@ export const createAnonymousDirectoryKey = (reference: string): string => {
33
35
 
34
36
  const isAnonymousResource = (name: string): boolean => name.startsWith(ANON_PREFIX);
35
37
 
36
- export namespace Resources {
37
- export type Service = {
38
- load: (
39
- name: string,
40
- options?: {
41
- quiet?: boolean;
42
- }
43
- ) => Promise<BtcaFsResource>;
44
- };
38
+ export type ResourcesService = {
39
+ load: (
40
+ name: string,
41
+ options?: {
42
+ quiet?: boolean;
43
+ }
44
+ ) => Promise<BtcaFsResource>;
45
+ loadEffect: (
46
+ name: string,
47
+ options?: {
48
+ quiet?: boolean;
49
+ }
50
+ ) => Effect.Effect<BtcaFsResource, ResourceError>;
51
+ };
45
52
 
46
- const normalizeSearchPaths = (definition: GitResource): string[] => {
47
- const paths = [
48
- ...(definition.searchPaths ?? []),
49
- ...(definition.searchPath ? [definition.searchPath] : [])
50
- ];
51
- return paths.filter((path) => path.trim().length > 0);
52
- };
53
+ const normalizeSearchPaths = (definition: GitResource): string[] => {
54
+ const paths = [
55
+ ...(definition.searchPaths ?? []),
56
+ ...(definition.searchPath ? [definition.searchPath] : [])
57
+ ];
58
+ return paths.filter((path) => path.trim().length > 0);
59
+ };
53
60
 
54
- const definitionToGitArgs = (
55
- definition: GitResource,
56
- resourcesDirectory: string,
57
- quiet: boolean
58
- ): BtcaGitResourceArgs => ({
59
- type: 'git',
61
+ const definitionToGitArgs = (
62
+ definition: GitResource,
63
+ resourcesDirectory: string,
64
+ quiet: boolean
65
+ ): BtcaGitResourceArgs => ({
66
+ type: 'git',
67
+ name: definition.name,
68
+ url: definition.url,
69
+ branch: definition.branch,
70
+ repoSubPaths: normalizeSearchPaths(definition),
71
+ resourcesDirectoryPath: resourcesDirectory,
72
+ specialAgentInstructions: definition.specialNotes ?? '',
73
+ quiet,
74
+ ephemeral: isAnonymousResource(definition.name),
75
+ localDirectoryKey: isAnonymousResource(definition.name)
76
+ ? createAnonymousDirectoryKey(definition.url)
77
+ : undefined
78
+ });
79
+
80
+ const definitionToLocalArgs = (definition: LocalResource): BtcaLocalResourceArgs => ({
81
+ type: 'local',
82
+ name: definition.name,
83
+ path: definition.path,
84
+ specialAgentInstructions: definition.specialNotes ?? ''
85
+ });
86
+
87
+ const definitionToNpmArgs = (
88
+ definition: NpmResource,
89
+ resourcesDirectory: string
90
+ ): BtcaNpmResourceArgs => {
91
+ const reference = `${definition.package}${definition.version ? `@${definition.version}` : ''}`;
92
+ return {
93
+ type: 'npm',
60
94
  name: definition.name,
61
- url: definition.url,
62
- branch: definition.branch,
63
- repoSubPaths: normalizeSearchPaths(definition),
95
+ package: definition.package,
96
+ ...(definition.version ? { version: definition.version } : {}),
64
97
  resourcesDirectoryPath: resourcesDirectory,
65
98
  specialAgentInstructions: definition.specialNotes ?? '',
66
- quiet,
67
99
  ephemeral: isAnonymousResource(definition.name),
68
100
  localDirectoryKey: isAnonymousResource(definition.name)
69
- ? createAnonymousDirectoryKey(definition.url)
101
+ ? createAnonymousDirectoryKey(reference)
70
102
  : undefined
71
- });
72
-
73
- const definitionToLocalArgs = (definition: LocalResource): BtcaLocalResourceArgs => ({
74
- type: 'local',
75
- name: definition.name,
76
- path: definition.path,
77
- specialAgentInstructions: definition.specialNotes ?? ''
78
- });
103
+ };
104
+ };
79
105
 
80
- const definitionToNpmArgs = (
81
- definition: NpmResource,
82
- resourcesDirectory: string
83
- ): BtcaNpmResourceArgs => {
84
- const reference = `${definition.package}${definition.version ? `@${definition.version}` : ''}`;
106
+ const loadLocalResource = (args: BtcaLocalResourceArgs): BtcaFsResource => ({
107
+ _tag: 'fs-based',
108
+ name: args.name,
109
+ fsName: resourceNameToKey(args.name),
110
+ type: 'local',
111
+ repoSubPaths: [],
112
+ specialAgentInstructions: args.specialAgentInstructions,
113
+ getAbsoluteDirectoryPath: async () => args.path
114
+ });
115
+
116
+ export const createAnonymousResource = (reference: string): ResourceDefinition | null => {
117
+ const npmReference = parseNpmReference(reference);
118
+ if (npmReference) {
85
119
  return {
86
120
  type: 'npm',
87
- name: definition.name,
88
- package: definition.package,
89
- ...(definition.version ? { version: definition.version } : {}),
90
- resourcesDirectoryPath: resourcesDirectory,
91
- specialAgentInstructions: definition.specialNotes ?? '',
92
- ephemeral: isAnonymousResource(definition.name),
93
- localDirectoryKey: isAnonymousResource(definition.name)
94
- ? createAnonymousDirectoryKey(reference)
95
- : undefined
121
+ name: `${ANON_PREFIX}${npmReference.normalizedReference}`,
122
+ package: npmReference.packageName,
123
+ ...(npmReference.version ? { version: npmReference.version } : {})
96
124
  };
97
- };
98
-
99
- const loadLocalResource = (args: BtcaLocalResourceArgs): BtcaFsResource => ({
100
- _tag: 'fs-based',
101
- name: args.name,
102
- fsName: resourceNameToKey(args.name),
103
- type: 'local',
104
- repoSubPaths: [],
105
- specialAgentInstructions: args.specialAgentInstructions,
106
- getAbsoluteDirectoryPath: async () => args.path
107
- });
125
+ }
108
126
 
109
- export const createAnonymousResource = (reference: string): ResourceDefinition | null => {
110
- const npmReference = parseNpmReference(reference);
111
- if (npmReference) {
112
- return {
113
- type: 'npm',
114
- name: `${ANON_PREFIX}${npmReference.normalizedReference}`,
115
- package: npmReference.packageName,
116
- ...(npmReference.version ? { version: npmReference.version } : {})
117
- };
118
- }
119
-
120
- const gitUrlResult = validateGitUrl(reference);
121
- if (gitUrlResult.valid) {
122
- const normalizedUrl = gitUrlResult.value;
123
- return {
124
- type: 'git',
125
- name: `${ANON_PREFIX}${normalizedUrl}`,
126
- url: normalizedUrl,
127
- branch: DEFAULT_ANON_BRANCH
128
- };
129
- }
130
- return null;
131
- };
127
+ const gitUrlResult = validateGitUrl(reference);
128
+ if (gitUrlResult.valid) {
129
+ const normalizedUrl = gitUrlResult.value;
130
+ return {
131
+ type: 'git',
132
+ name: `${ANON_PREFIX}${normalizedUrl}`,
133
+ url: normalizedUrl,
134
+ branch: DEFAULT_ANON_BRANCH
135
+ };
136
+ }
137
+ return null;
138
+ };
132
139
 
133
- export const resolveResourceDefinition = (
134
- reference: string,
135
- getResource: Config.Service['getResource']
136
- ): ResourceDefinition => {
137
- const definition = getResource(reference);
138
- if (definition) return definition;
140
+ export const resolveResourceDefinition = (
141
+ reference: string,
142
+ getResource: ConfigServiceShape['getResource']
143
+ ): ResourceDefinition => {
144
+ const definition = getResource(reference);
145
+ if (definition) return definition;
139
146
 
140
- const anonymousDefinition = createAnonymousResource(reference);
141
- if (anonymousDefinition) return anonymousDefinition;
147
+ const anonymousDefinition = createAnonymousResource(reference);
148
+ if (anonymousDefinition) return anonymousDefinition;
142
149
 
143
- throw new ResourceError({
144
- message: `Resource "${reference}" not found in config`,
145
- hint: `${CommonHints.LIST_RESOURCES} ${CommonHints.ADD_RESOURCE}`
146
- });
147
- };
150
+ throw new ResourceError({
151
+ message: `Resource "${reference}" not found in config`,
152
+ hint: `${CommonHints.LIST_RESOURCES} ${CommonHints.ADD_RESOURCE}`
153
+ });
154
+ };
148
155
 
149
- export const create = (config: Config.Service): Service => {
150
- return {
151
- load: async (name, options) => {
152
- const quiet = options?.quiet ?? false;
153
- const definition = resolveResourceDefinition(name, config.getResource);
156
+ export const createResourcesService = (config: ConfigServiceShape): ResourcesService => {
157
+ const loadEffect: ResourcesService['loadEffect'] = (name, options) =>
158
+ Effect.gen(function* () {
159
+ const quiet = options?.quiet ?? false;
160
+ const definition = yield* Effect.try({
161
+ try: () => resolveResourceDefinition(name, config.getResource),
162
+ catch: (cause) =>
163
+ cause instanceof ResourceError
164
+ ? cause
165
+ : new ResourceError({
166
+ message: `Failed to resolve resource "${name}"`,
167
+ hint: `${CommonHints.LIST_RESOURCES} ${CommonHints.ADD_RESOURCE}`,
168
+ cause
169
+ })
170
+ });
171
+
172
+ if (isGitResource(definition)) {
173
+ return yield* Effect.tryPromise({
174
+ try: () =>
175
+ loadGitResource(definitionToGitArgs(definition, config.resourcesDirectory, quiet)),
176
+ catch: (cause) =>
177
+ cause instanceof ResourceError
178
+ ? cause
179
+ : new ResourceError({
180
+ message: `Failed to load git resource "${name}"`,
181
+ hint: CommonHints.CLEAR_CACHE,
182
+ cause
183
+ })
184
+ });
185
+ }
154
186
 
155
- if (isGitResource(definition)) {
156
- return loadGitResource(definitionToGitArgs(definition, config.resourcesDirectory, quiet));
157
- }
187
+ if (isNpmResource(definition)) {
188
+ return yield* Effect.tryPromise({
189
+ try: () => loadNpmResource(definitionToNpmArgs(definition, config.resourcesDirectory)),
190
+ catch: (cause) =>
191
+ cause instanceof ResourceError
192
+ ? cause
193
+ : new ResourceError({
194
+ message: `Failed to load npm resource "${name}"`,
195
+ hint: CommonHints.CLEAR_CACHE,
196
+ cause
197
+ })
198
+ });
199
+ }
158
200
 
159
- if (isNpmResource(definition)) {
160
- return loadNpmResource(definitionToNpmArgs(definition, config.resourcesDirectory));
161
- }
201
+ return loadLocalResource(definitionToLocalArgs(definition));
202
+ });
162
203
 
163
- return loadLocalResource(definitionToLocalArgs(definition));
164
- }
165
- };
204
+ return {
205
+ load: (name, options) => Effect.runPromise(loadEffect(name, options)),
206
+ loadEffect
166
207
  };
167
- }
208
+ };
@@ -1,4 +1,4 @@
1
- export { StreamService } from './service.ts';
1
+ export { createSseStream } from './service.ts';
2
2
  export {
3
3
  BtcaStreamEventSchema,
4
4
  BtcaStreamMetaEventSchema,
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect } from 'bun:test';
2
2
 
3
- import { StreamService } from './service.ts';
3
+ import { createSseStream } from './service.ts';
4
4
  import type { BtcaStreamEvent } from './types.ts';
5
5
 
6
6
  const readStream = async (stream: ReadableStream<Uint8Array>) => {
@@ -22,7 +22,7 @@ const parseSseEvents = (payload: string) =>
22
22
  .filter((line): line is string => Boolean(line))
23
23
  .map((line) => JSON.parse(line.slice(6)) as BtcaStreamEvent);
24
24
 
25
- describe('StreamService.createSseStream', () => {
25
+ describe('createSseStream', () => {
26
26
  it('streams reasoning deltas and includes final reasoning in done', async () => {
27
27
  const eventStream = (async function* () {
28
28
  yield { type: 'reasoning-delta', text: 'First ' } as const;
@@ -31,7 +31,7 @@ describe('StreamService.createSseStream', () => {
31
31
  yield { type: 'finish', finishReason: 'stop' } as const;
32
32
  })();
33
33
 
34
- const stream = StreamService.createSseStream({
34
+ const stream = createSseStream({
35
35
  meta: {
36
36
  type: 'meta',
37
37
  model: { provider: 'test', model: 'test-model' },
@@ -71,7 +71,7 @@ describe('StreamService.createSseStream', () => {
71
71
  } as const;
72
72
  })();
73
73
 
74
- const stream = StreamService.createSseStream({
74
+ const stream = createSseStream({
75
75
  meta: {
76
76
  type: 'meta',
77
77
  model: { provider: 'openrouter', model: 'openai/gpt-4o-mini' },
@@ -124,7 +124,7 @@ describe('StreamService.createSseStream', () => {
124
124
  yield { type: 'finish', finishReason: 'stop' } as const;
125
125
  })();
126
126
 
127
- const stream = StreamService.createSseStream({
127
+ const stream = createSseStream({
128
128
  meta: {
129
129
  type: 'meta',
130
130
  model: { provider: 'test', model: 'test-model' },