@zhachory1/mewrite-ai 0.65.3
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/README.md +1330 -0
- package/dist/api-registry.d.ts +20 -0
- package/dist/api-registry.d.ts.map +1 -0
- package/dist/api-registry.js +44 -0
- package/dist/api-registry.js.map +1 -0
- package/dist/bedrock-provider.d.ts +5 -0
- package/dist/bedrock-provider.d.ts.map +1 -0
- package/dist/bedrock-provider.js +6 -0
- package/dist/bedrock-provider.js.map +1 -0
- package/dist/cache/__tests__/compaction.test.d.ts +2 -0
- package/dist/cache/__tests__/compaction.test.d.ts.map +1 -0
- package/dist/cache/__tests__/compaction.test.js +100 -0
- package/dist/cache/__tests__/compaction.test.js.map +1 -0
- package/dist/cache/__tests__/layers.test.d.ts +2 -0
- package/dist/cache/__tests__/layers.test.d.ts.map +1 -0
- package/dist/cache/__tests__/layers.test.js +71 -0
- package/dist/cache/__tests__/layers.test.js.map +1 -0
- package/dist/cache/__tests__/policy.test.d.ts +2 -0
- package/dist/cache/__tests__/policy.test.d.ts.map +1 -0
- package/dist/cache/__tests__/policy.test.js +79 -0
- package/dist/cache/__tests__/policy.test.js.map +1 -0
- package/dist/cache/__tests__/tool-serializer.test.d.ts +2 -0
- package/dist/cache/__tests__/tool-serializer.test.d.ts.map +1 -0
- package/dist/cache/__tests__/tool-serializer.test.js +64 -0
- package/dist/cache/__tests__/tool-serializer.test.js.map +1 -0
- package/dist/cache/compaction.d.ts +30 -0
- package/dist/cache/compaction.d.ts.map +1 -0
- package/dist/cache/compaction.js +50 -0
- package/dist/cache/compaction.js.map +1 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/layers.d.ts +17 -0
- package/dist/cache/layers.d.ts.map +1 -0
- package/dist/cache/layers.js +48 -0
- package/dist/cache/layers.js.map +1 -0
- package/dist/cache/policy.d.ts +40 -0
- package/dist/cache/policy.d.ts.map +1 -0
- package/dist/cache/policy.js +50 -0
- package/dist/cache/policy.js.map +1 -0
- package/dist/cache/tool-serializer.d.ts +8 -0
- package/dist/cache/tool-serializer.d.ts.map +1 -0
- package/dist/cache/tool-serializer.js +35 -0
- package/dist/cache/tool-serializer.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +116 -0
- package/dist/cli.js.map +1 -0
- package/dist/env-api-keys.d.ts +27 -0
- package/dist/env-api-keys.d.ts.map +1 -0
- package/dist/env-api-keys.js +223 -0
- package/dist/env-api-keys.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +34 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.generated.d.ts +14370 -0
- package/dist/models.generated.d.ts.map +1 -0
- package/dist/models.generated.js +14181 -0
- package/dist/models.generated.js.map +1 -0
- package/dist/models.js +140 -0
- package/dist/models.js.map +1 -0
- package/dist/oauth.d.ts +2 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +2 -0
- package/dist/oauth.js.map +1 -0
- package/dist/providers/amazon-bedrock.d.ts +20 -0
- package/dist/providers/amazon-bedrock.d.ts.map +1 -0
- package/dist/providers/amazon-bedrock.js +651 -0
- package/dist/providers/amazon-bedrock.js.map +1 -0
- package/dist/providers/anthropic-capabilities.d.ts +65 -0
- package/dist/providers/anthropic-capabilities.d.ts.map +1 -0
- package/dist/providers/anthropic-capabilities.js +155 -0
- package/dist/providers/anthropic-capabilities.js.map +1 -0
- package/dist/providers/anthropic-discovery.d.ts +49 -0
- package/dist/providers/anthropic-discovery.d.ts.map +1 -0
- package/dist/providers/anthropic-discovery.js +218 -0
- package/dist/providers/anthropic-discovery.js.map +1 -0
- package/dist/providers/anthropic.d.ts +40 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +766 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/azure-openai-responses.d.ts +15 -0
- package/dist/providers/azure-openai-responses.d.ts.map +1 -0
- package/dist/providers/azure-openai-responses.js +176 -0
- package/dist/providers/azure-openai-responses.js.map +1 -0
- package/dist/providers/faux.d.ts +56 -0
- package/dist/providers/faux.d.ts.map +1 -0
- package/dist/providers/faux.js +367 -0
- package/dist/providers/faux.js.map +1 -0
- package/dist/providers/github-copilot-headers.d.ts +8 -0
- package/dist/providers/github-copilot-headers.d.ts.map +1 -0
- package/dist/providers/github-copilot-headers.js +29 -0
- package/dist/providers/github-copilot-headers.js.map +1 -0
- package/dist/providers/google-gemini-cli.d.ts +74 -0
- package/dist/providers/google-gemini-cli.d.ts.map +1 -0
- package/dist/providers/google-gemini-cli.js +776 -0
- package/dist/providers/google-gemini-cli.js.map +1 -0
- package/dist/providers/google-shared.d.ts +65 -0
- package/dist/providers/google-shared.d.ts.map +1 -0
- package/dist/providers/google-shared.js +312 -0
- package/dist/providers/google-shared.js.map +1 -0
- package/dist/providers/google-vertex.d.ts +15 -0
- package/dist/providers/google-vertex.d.ts.map +1 -0
- package/dist/providers/google-vertex.js +419 -0
- package/dist/providers/google-vertex.js.map +1 -0
- package/dist/providers/google.d.ts +13 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +374 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/mistral.d.ts +22 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/mistral.js +501 -0
- package/dist/providers/mistral.js.map +1 -0
- package/dist/providers/openai-codex-responses.d.ts +9 -0
- package/dist/providers/openai-codex-responses.d.ts.map +1 -0
- package/dist/providers/openai-codex-responses.js +741 -0
- package/dist/providers/openai-codex-responses.js.map +1 -0
- package/dist/providers/openai-completions.d.ts +15 -0
- package/dist/providers/openai-completions.d.ts.map +1 -0
- package/dist/providers/openai-completions.js +753 -0
- package/dist/providers/openai-completions.js.map +1 -0
- package/dist/providers/openai-responses-shared.d.ts +17 -0
- package/dist/providers/openai-responses-shared.d.ts.map +1 -0
- package/dist/providers/openai-responses-shared.js +470 -0
- package/dist/providers/openai-responses-shared.js.map +1 -0
- package/dist/providers/openai-responses.d.ts +13 -0
- package/dist/providers/openai-responses.d.ts.map +1 -0
- package/dist/providers/openai-responses.js +190 -0
- package/dist/providers/openai-responses.js.map +1 -0
- package/dist/providers/register-builtins.d.ts +38 -0
- package/dist/providers/register-builtins.d.ts.map +1 -0
- package/dist/providers/register-builtins.js +261 -0
- package/dist/providers/register-builtins.js.map +1 -0
- package/dist/providers/simple-options.d.ts +8 -0
- package/dist/providers/simple-options.d.ts.map +1 -0
- package/dist/providers/simple-options.js +35 -0
- package/dist/providers/simple-options.js.map +1 -0
- package/dist/providers/transform-messages.d.ts +8 -0
- package/dist/providers/transform-messages.d.ts.map +1 -0
- package/dist/providers/transform-messages.js +155 -0
- package/dist/providers/transform-messages.js.map +1 -0
- package/dist/registry/fetcher.d.ts +26 -0
- package/dist/registry/fetcher.d.ts.map +1 -0
- package/dist/registry/fetcher.js +69 -0
- package/dist/registry/fetcher.js.map +1 -0
- package/dist/registry/index.d.ts +8 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/loader.d.ts +38 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +90 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/merger.d.ts +21 -0
- package/dist/registry/merger.d.ts.map +1 -0
- package/dist/registry/merger.js +90 -0
- package/dist/registry/merger.js.map +1 -0
- package/dist/registry/schema.d.ts +130 -0
- package/dist/registry/schema.d.ts.map +1 -0
- package/dist/registry/schema.js +103 -0
- package/dist/registry/schema.js.map +1 -0
- package/dist/stream.d.ts +8 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +27 -0
- package/dist/stream.js.map +1 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/event-stream.d.ts +21 -0
- package/dist/utils/event-stream.d.ts.map +1 -0
- package/dist/utils/event-stream.js +81 -0
- package/dist/utils/event-stream.js.map +1 -0
- package/dist/utils/hash.d.ts +3 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +14 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/json-parse.d.ts +9 -0
- package/dist/utils/json-parse.d.ts.map +1 -0
- package/dist/utils/json-parse.js +29 -0
- package/dist/utils/json-parse.js.map +1 -0
- package/dist/utils/oauth/anthropic.d.ts +25 -0
- package/dist/utils/oauth/anthropic.d.ts.map +1 -0
- package/dist/utils/oauth/anthropic.js +336 -0
- package/dist/utils/oauth/anthropic.js.map +1 -0
- package/dist/utils/oauth/github-copilot.d.ts +30 -0
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -0
- package/dist/utils/oauth/github-copilot.js +293 -0
- package/dist/utils/oauth/github-copilot.js.map +1 -0
- package/dist/utils/oauth/google-antigravity.d.ts +26 -0
- package/dist/utils/oauth/google-antigravity.d.ts.map +1 -0
- package/dist/utils/oauth/google-antigravity.js +376 -0
- package/dist/utils/oauth/google-antigravity.js.map +1 -0
- package/dist/utils/oauth/google-gemini-cli.d.ts +26 -0
- package/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -0
- package/dist/utils/oauth/google-gemini-cli.js +482 -0
- package/dist/utils/oauth/google-gemini-cli.js.map +1 -0
- package/dist/utils/oauth/index.d.ts +61 -0
- package/dist/utils/oauth/index.d.ts.map +1 -0
- package/dist/utils/oauth/index.js +131 -0
- package/dist/utils/oauth/index.js.map +1 -0
- package/dist/utils/oauth/oauth-page.d.ts +3 -0
- package/dist/utils/oauth/oauth-page.d.ts.map +1 -0
- package/dist/utils/oauth/oauth-page.js +105 -0
- package/dist/utils/oauth/oauth-page.js.map +1 -0
- package/dist/utils/oauth/openai-codex.d.ts +34 -0
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -0
- package/dist/utils/oauth/openai-codex.js +374 -0
- package/dist/utils/oauth/openai-codex.js.map +1 -0
- package/dist/utils/oauth/pkce.d.ts +13 -0
- package/dist/utils/oauth/pkce.d.ts.map +1 -0
- package/dist/utils/oauth/pkce.js +31 -0
- package/dist/utils/oauth/pkce.js.map +1 -0
- package/dist/utils/oauth/types.d.ts +49 -0
- package/dist/utils/oauth/types.d.ts.map +1 -0
- package/dist/utils/oauth/types.js +2 -0
- package/dist/utils/oauth/types.js.map +1 -0
- package/dist/utils/overflow.d.ts +53 -0
- package/dist/utils/overflow.d.ts.map +1 -0
- package/dist/utils/overflow.js +132 -0
- package/dist/utils/overflow.js.map +1 -0
- package/dist/utils/sanitize-unicode.d.ts +22 -0
- package/dist/utils/sanitize-unicode.d.ts.map +1 -0
- package/dist/utils/sanitize-unicode.js +26 -0
- package/dist/utils/sanitize-unicode.js.map +1 -0
- package/dist/utils/typebox-helpers.d.ts +17 -0
- package/dist/utils/typebox-helpers.d.ts.map +1 -0
- package/dist/utils/typebox-helpers.js +21 -0
- package/dist/utils/typebox-helpers.js.map +1 -0
- package/dist/utils/validation.d.ts +18 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +80 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +129 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Api, AssistantMessageEventStream, Context, Model, SimpleStreamOptions, StreamFunction, StreamOptions } from "./types.js";
|
|
2
|
+
export type ApiStreamFunction = (model: Model<Api>, context: Context, options?: StreamOptions) => AssistantMessageEventStream;
|
|
3
|
+
export type ApiStreamSimpleFunction = (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
|
|
4
|
+
export interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {
|
|
5
|
+
api: TApi;
|
|
6
|
+
stream: StreamFunction<TApi, TOptions>;
|
|
7
|
+
streamSimple: StreamFunction<TApi, SimpleStreamOptions>;
|
|
8
|
+
}
|
|
9
|
+
interface ApiProviderInternal {
|
|
10
|
+
api: Api;
|
|
11
|
+
stream: ApiStreamFunction;
|
|
12
|
+
streamSimple: ApiStreamSimpleFunction;
|
|
13
|
+
}
|
|
14
|
+
export declare function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(provider: ApiProvider<TApi, TOptions>, sourceId?: string): void;
|
|
15
|
+
export declare function getApiProvider(api: Api): ApiProviderInternal | undefined;
|
|
16
|
+
export declare function getApiProviders(): ApiProviderInternal[];
|
|
17
|
+
export declare function unregisterApiProviders(sourceId: string): void;
|
|
18
|
+
export declare function clearApiProviders(): void;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=api-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-registry.d.ts","sourceRoot":"","sources":["../src/api-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,GAAG,EACH,2BAA2B,EAC3B,OAAO,EACP,KAAK,EACL,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,iBAAiB,GAAG,CAC/B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,aAAa,KACnB,2BAA2B,CAAC;AAEjC,MAAM,MAAM,uBAAuB,GAAG,CACrC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,KACzB,2BAA2B,CAAC;AAEjC,MAAM,WAAW,WAAW,CAAC,IAAI,SAAS,GAAG,GAAG,GAAG,EAAE,QAAQ,SAAS,aAAa,GAAG,aAAa;IAClG,GAAG,EAAE,IAAI,CAAC;IACV,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,YAAY,EAAE,cAAc,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;CACxD;AAED,UAAU,mBAAmB;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,uBAAuB,CAAC;CACtC;AAiCD,wBAAgB,mBAAmB,CAAC,IAAI,SAAS,GAAG,EAAE,QAAQ,SAAS,aAAa,EACnF,QAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,EACrC,QAAQ,CAAC,EAAE,MAAM,GACf,IAAI,CASN;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,mBAAmB,GAAG,SAAS,CAExE;AAED,wBAAgB,eAAe,IAAI,mBAAmB,EAAE,CAEvD;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAM7D;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC","sourcesContent":["import type {\n\tApi,\n\tAssistantMessageEventStream,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n} from \"./types.js\";\n\nexport type ApiStreamFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: StreamOptions,\n) => AssistantMessageEventStream;\n\nexport type ApiStreamSimpleFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n) => AssistantMessageEventStream;\n\nexport interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {\n\tapi: TApi;\n\tstream: StreamFunction<TApi, TOptions>;\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>;\n}\n\ninterface ApiProviderInternal {\n\tapi: Api;\n\tstream: ApiStreamFunction;\n\tstreamSimple: ApiStreamSimpleFunction;\n}\n\ntype RegisteredApiProvider = {\n\tprovider: ApiProviderInternal;\n\tsourceId?: string;\n};\n\nconst apiProviderRegistry = new Map<string, RegisteredApiProvider>();\n\nfunction wrapStream<TApi extends Api, TOptions extends StreamOptions>(\n\tapi: TApi,\n\tstream: StreamFunction<TApi, TOptions>,\n): ApiStreamFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn stream(model as Model<TApi>, context, options as TOptions);\n\t};\n}\n\nfunction wrapStreamSimple<TApi extends Api>(\n\tapi: TApi,\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>,\n): ApiStreamSimpleFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn streamSimple(model as Model<TApi>, context, options);\n\t};\n}\n\nexport function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(\n\tprovider: ApiProvider<TApi, TOptions>,\n\tsourceId?: string,\n): void {\n\tapiProviderRegistry.set(provider.api, {\n\t\tprovider: {\n\t\t\tapi: provider.api,\n\t\t\tstream: wrapStream(provider.api, provider.stream),\n\t\t\tstreamSimple: wrapStreamSimple(provider.api, provider.streamSimple),\n\t\t},\n\t\tsourceId,\n\t});\n}\n\nexport function getApiProvider(api: Api): ApiProviderInternal | undefined {\n\treturn apiProviderRegistry.get(api)?.provider;\n}\n\nexport function getApiProviders(): ApiProviderInternal[] {\n\treturn Array.from(apiProviderRegistry.values(), (entry) => entry.provider);\n}\n\nexport function unregisterApiProviders(sourceId: string): void {\n\tfor (const [api, entry] of apiProviderRegistry.entries()) {\n\t\tif (entry.sourceId === sourceId) {\n\t\t\tapiProviderRegistry.delete(api);\n\t\t}\n\t}\n}\n\nexport function clearApiProviders(): void {\n\tapiProviderRegistry.clear();\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const apiProviderRegistry = new Map();
|
|
2
|
+
function wrapStream(api, stream) {
|
|
3
|
+
return (model, context, options) => {
|
|
4
|
+
if (model.api !== api) {
|
|
5
|
+
throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
6
|
+
}
|
|
7
|
+
return stream(model, context, options);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function wrapStreamSimple(api, streamSimple) {
|
|
11
|
+
return (model, context, options) => {
|
|
12
|
+
if (model.api !== api) {
|
|
13
|
+
throw new Error(`Mismatched api: ${model.api} expected ${api}`);
|
|
14
|
+
}
|
|
15
|
+
return streamSimple(model, context, options);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function registerApiProvider(provider, sourceId) {
|
|
19
|
+
apiProviderRegistry.set(provider.api, {
|
|
20
|
+
provider: {
|
|
21
|
+
api: provider.api,
|
|
22
|
+
stream: wrapStream(provider.api, provider.stream),
|
|
23
|
+
streamSimple: wrapStreamSimple(provider.api, provider.streamSimple),
|
|
24
|
+
},
|
|
25
|
+
sourceId,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function getApiProvider(api) {
|
|
29
|
+
return apiProviderRegistry.get(api)?.provider;
|
|
30
|
+
}
|
|
31
|
+
export function getApiProviders() {
|
|
32
|
+
return Array.from(apiProviderRegistry.values(), (entry) => entry.provider);
|
|
33
|
+
}
|
|
34
|
+
export function unregisterApiProviders(sourceId) {
|
|
35
|
+
for (const [api, entry] of apiProviderRegistry.entries()) {
|
|
36
|
+
if (entry.sourceId === sourceId) {
|
|
37
|
+
apiProviderRegistry.delete(api);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function clearApiProviders() {
|
|
42
|
+
apiProviderRegistry.clear();
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=api-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-registry.js","sourceRoot":"","sources":["../src/api-registry.ts"],"names":[],"mappings":"AAuCA,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAiC,CAAC;AAErE,SAAS,UAAU,CAClB,GAAS,EACT,MAAsC,EAClB;IACpB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,GAAG,aAAa,GAAG,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,MAAM,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAmB,CAAC,CAAC;IAAA,CAClE,CAAC;AAAA,CACF;AAED,SAAS,gBAAgB,CACxB,GAAS,EACT,YAAuD,EAC7B;IAC1B,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,CAAC,GAAG,aAAa,GAAG,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,YAAY,CAAC,KAAoB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAAA,CAC5D,CAAC;AAAA,CACF;AAED,MAAM,UAAU,mBAAmB,CAClC,QAAqC,EACrC,QAAiB,EACV;IACP,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE;QACrC,QAAQ,EAAE;YACT,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC;YACjD,YAAY,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,YAAY,CAAC;SACnE;QACD,QAAQ;KACR,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,cAAc,CAAC,GAAQ,EAAmC;IACzE,OAAO,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;AAAA,CAC9C;AAED,MAAM,UAAU,eAAe,GAA0B;IACxD,OAAO,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;AAAA,CAC3E;AAED,MAAM,UAAU,sBAAsB,CAAC,QAAgB,EAAQ;IAC9D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1D,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;AAAA,CACD;AAED,MAAM,UAAU,iBAAiB,GAAS;IACzC,mBAAmB,CAAC,KAAK,EAAE,CAAC;AAAA,CAC5B","sourcesContent":["import type {\n\tApi,\n\tAssistantMessageEventStream,\n\tContext,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n} from \"./types.js\";\n\nexport type ApiStreamFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: StreamOptions,\n) => AssistantMessageEventStream;\n\nexport type ApiStreamSimpleFunction = (\n\tmodel: Model<Api>,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n) => AssistantMessageEventStream;\n\nexport interface ApiProvider<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> {\n\tapi: TApi;\n\tstream: StreamFunction<TApi, TOptions>;\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>;\n}\n\ninterface ApiProviderInternal {\n\tapi: Api;\n\tstream: ApiStreamFunction;\n\tstreamSimple: ApiStreamSimpleFunction;\n}\n\ntype RegisteredApiProvider = {\n\tprovider: ApiProviderInternal;\n\tsourceId?: string;\n};\n\nconst apiProviderRegistry = new Map<string, RegisteredApiProvider>();\n\nfunction wrapStream<TApi extends Api, TOptions extends StreamOptions>(\n\tapi: TApi,\n\tstream: StreamFunction<TApi, TOptions>,\n): ApiStreamFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn stream(model as Model<TApi>, context, options as TOptions);\n\t};\n}\n\nfunction wrapStreamSimple<TApi extends Api>(\n\tapi: TApi,\n\tstreamSimple: StreamFunction<TApi, SimpleStreamOptions>,\n): ApiStreamSimpleFunction {\n\treturn (model, context, options) => {\n\t\tif (model.api !== api) {\n\t\t\tthrow new Error(`Mismatched api: ${model.api} expected ${api}`);\n\t\t}\n\t\treturn streamSimple(model as Model<TApi>, context, options);\n\t};\n}\n\nexport function registerApiProvider<TApi extends Api, TOptions extends StreamOptions>(\n\tprovider: ApiProvider<TApi, TOptions>,\n\tsourceId?: string,\n): void {\n\tapiProviderRegistry.set(provider.api, {\n\t\tprovider: {\n\t\t\tapi: provider.api,\n\t\t\tstream: wrapStream(provider.api, provider.stream),\n\t\t\tstreamSimple: wrapStreamSimple(provider.api, provider.streamSimple),\n\t\t},\n\t\tsourceId,\n\t});\n}\n\nexport function getApiProvider(api: Api): ApiProviderInternal | undefined {\n\treturn apiProviderRegistry.get(api)?.provider;\n}\n\nexport function getApiProviders(): ApiProviderInternal[] {\n\treturn Array.from(apiProviderRegistry.values(), (entry) => entry.provider);\n}\n\nexport function unregisterApiProviders(sourceId: string): void {\n\tfor (const [api, entry] of apiProviderRegistry.entries()) {\n\t\tif (entry.sourceId === sourceId) {\n\t\t\tapiProviderRegistry.delete(api);\n\t\t}\n\t}\n}\n\nexport function clearApiProviders(): void {\n\tapiProviderRegistry.clear();\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const bedrockProviderModule: {
|
|
2
|
+
streamBedrock: import("./types.js").StreamFunction<"bedrock-converse-stream", import("./providers/amazon-bedrock.js").BedrockOptions>;
|
|
3
|
+
streamSimpleBedrock: import("./types.js").StreamFunction<"bedrock-converse-stream", import("./types.js").SimpleStreamOptions>;
|
|
4
|
+
};
|
|
5
|
+
//# sourceMappingURL=bedrock-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bedrock-provider.d.ts","sourceRoot":"","sources":["../src/bedrock-provider.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB;;;CAGjC,CAAC","sourcesContent":["import { streamBedrock, streamSimpleBedrock } from \"./providers/amazon-bedrock.js\";\n\nexport const bedrockProviderModule = {\n\tstreamBedrock,\n\tstreamSimpleBedrock,\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bedrock-provider.js","sourceRoot":"","sources":["../src/bedrock-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEnF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACpC,aAAa;IACb,mBAAmB;CACnB,CAAC","sourcesContent":["import { streamBedrock, streamSimpleBedrock } from \"./providers/amazon-bedrock.js\";\n\nexport const bedrockProviderModule = {\n\tstreamBedrock,\n\tstreamSimpleBedrock,\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.test.d.ts","sourceRoot":"","sources":["../../../src/cache/__tests__/compaction.test.ts"],"names":[],"mappings":"","sourcesContent":["// T-044..T-054\nimport { describe, expect, it } from \"vitest\";\nimport { KeepaliveScheduler, summarize, type Turn, trimMiddle } from \"../compaction.js\";\nimport { type CacheUsageReport, resolveRetention, totalInputTokens } from \"../policy.js\";\n\ndescribe(\"cache usage report (T-044, T-046)\", () => {\n\tit(\"exposes three token fields\", () => {\n\t\tconst u: CacheUsageReport = {\n\t\t\tcachedInputTokens: 100,\n\t\t\tcacheWriteTokens: 10,\n\t\t\tuncachedInputTokens: 50,\n\t\t};\n\t\texpect(u.cachedInputTokens).toBeDefined();\n\t\texpect(u.cacheWriteTokens).toBeDefined();\n\t\texpect(u.uncachedInputTokens).toBeDefined();\n\t});\n\n\tit(\"cached + uncached equals total input\", () => {\n\t\tconst u: CacheUsageReport = {\n\t\t\tcachedInputTokens: 300,\n\t\t\tcacheWriteTokens: 25,\n\t\t\tuncachedInputTokens: 200,\n\t\t};\n\t\texpect(totalInputTokens(u)).toBe(500);\n\t});\n});\n\ndescribe(\"resolveRetention (T-047, T-048, T-049)\", () => {\n\tit(\"defaults to role default when no override\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"long\" })).toBe(\"long\");\n\t});\n\n\tit(\"CLI flag overrides role default\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"long\", cliFlag: \"none\" })).toBe(\"none\");\n\t});\n});\n\ndescribe(\"trimMiddle (T-050, T-051, T-052)\", () => {\n\tfunction mkTurns(n: number): Turn[] {\n\t\treturn Array.from({ length: n }, (_, i) => ({\n\t\t\tindex: i,\n\t\t\tbytes: `turn-${i}`,\n\t\t}));\n\t}\n\n\tit(\"drops 1..24, preserves 25..30 with N=5 recent floor (30-turn history)\", () => {\n\t\tconst turns = mkTurns(30);\n\t\tconst result = trimMiddle(turns, { recentFloor: 5 });\n\t\texpect(result.kept.length).toBe(6); // summary + 5 recent\n\t\t// Last 5 kept\n\t\tconst recent = result.kept.slice(1);\n\t\texpect(recent.map((t) => t.index)).toEqual([25, 26, 27, 28, 29]);\n\t\t// Dropped 25 turns\n\t\texpect(result.droppedCount).toBe(25);\n\t});\n\n\tit(\"produces deterministic stable summary block\", () => {\n\t\tconst turns = mkTurns(20);\n\t\tconst a = trimMiddle(turns, { recentFloor: 5 }).summary;\n\t\tconst b = trimMiddle(turns, { recentFloor: 5 }).summary;\n\t\texpect(a.bytes).toBe(b.bytes);\n\t\texpect(a.bytes).toContain(\"compact\");\n\t});\n\n\tit(\"no-op when turns ≤ recentFloor + 1\", () => {\n\t\tconst turns = mkTurns(3);\n\t\tconst result = trimMiddle(turns, { recentFloor: 5 });\n\t\texpect(result.droppedCount).toBe(0);\n\t\texpect(result.kept).toEqual(turns);\n\t});\n\n\tit(\"summarize includes dropped indices for auditability\", () => {\n\t\tconst s = summarize([\n\t\t\t{ index: 1, bytes: \"a\" },\n\t\t\t{ index: 2, bytes: \"bb\" },\n\t\t]);\n\t\texpect(s).toContain(\"[1,2]\");\n\t\texpect(s).toContain(\"3 bytes\");\n\t});\n});\n\ndescribe(\"KeepaliveScheduler (T-053, T-054)\", () => {\n\tit(\"returns skip when retention=none\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(10_000, { intervalMs: 5_000, retention: \"none\", enabled: true })).toBe(\"skip\");\n\t});\n\n\tit(\"returns skip when disabled\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(10_000, { intervalMs: 5_000, retention: \"long\", enabled: false })).toBe(\"skip\");\n\t});\n\n\tit(\"returns ping after interval elapsed with retention=long\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(5_000, { intervalMs: 5_000, retention: \"long\", enabled: true })).toBe(\"ping\");\n\t});\n\n\tit(\"shuts off after idle 2x interval\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(11_000, { intervalMs: 5_000, retention: \"long\", enabled: true })).toBe(\"shutoff\");\n\t});\n\n\tit(\"default off: enabled=false skips even when interval passed\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\tconst r = k.tick(10_000, { intervalMs: 5_000, retention: \"long\", enabled: false });\n\t\texpect(r).toBe(\"skip\");\n\t});\n});\n"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// T-044..T-054
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { KeepaliveScheduler, summarize, trimMiddle } from "../compaction.js";
|
|
4
|
+
import { resolveRetention, totalInputTokens } from "../policy.js";
|
|
5
|
+
describe("cache usage report (T-044, T-046)", () => {
|
|
6
|
+
it("exposes three token fields", () => {
|
|
7
|
+
const u = {
|
|
8
|
+
cachedInputTokens: 100,
|
|
9
|
+
cacheWriteTokens: 10,
|
|
10
|
+
uncachedInputTokens: 50,
|
|
11
|
+
};
|
|
12
|
+
expect(u.cachedInputTokens).toBeDefined();
|
|
13
|
+
expect(u.cacheWriteTokens).toBeDefined();
|
|
14
|
+
expect(u.uncachedInputTokens).toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
it("cached + uncached equals total input", () => {
|
|
17
|
+
const u = {
|
|
18
|
+
cachedInputTokens: 300,
|
|
19
|
+
cacheWriteTokens: 25,
|
|
20
|
+
uncachedInputTokens: 200,
|
|
21
|
+
};
|
|
22
|
+
expect(totalInputTokens(u)).toBe(500);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe("resolveRetention (T-047, T-048, T-049)", () => {
|
|
26
|
+
it("defaults to role default when no override", () => {
|
|
27
|
+
expect(resolveRetention({ roleDefault: "long" })).toBe("long");
|
|
28
|
+
});
|
|
29
|
+
it("CLI flag overrides role default", () => {
|
|
30
|
+
expect(resolveRetention({ roleDefault: "long", cliFlag: "none" })).toBe("none");
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe("trimMiddle (T-050, T-051, T-052)", () => {
|
|
34
|
+
function mkTurns(n) {
|
|
35
|
+
return Array.from({ length: n }, (_, i) => ({
|
|
36
|
+
index: i,
|
|
37
|
+
bytes: `turn-${i}`,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
it("drops 1..24, preserves 25..30 with N=5 recent floor (30-turn history)", () => {
|
|
41
|
+
const turns = mkTurns(30);
|
|
42
|
+
const result = trimMiddle(turns, { recentFloor: 5 });
|
|
43
|
+
expect(result.kept.length).toBe(6); // summary + 5 recent
|
|
44
|
+
// Last 5 kept
|
|
45
|
+
const recent = result.kept.slice(1);
|
|
46
|
+
expect(recent.map((t) => t.index)).toEqual([25, 26, 27, 28, 29]);
|
|
47
|
+
// Dropped 25 turns
|
|
48
|
+
expect(result.droppedCount).toBe(25);
|
|
49
|
+
});
|
|
50
|
+
it("produces deterministic stable summary block", () => {
|
|
51
|
+
const turns = mkTurns(20);
|
|
52
|
+
const a = trimMiddle(turns, { recentFloor: 5 }).summary;
|
|
53
|
+
const b = trimMiddle(turns, { recentFloor: 5 }).summary;
|
|
54
|
+
expect(a.bytes).toBe(b.bytes);
|
|
55
|
+
expect(a.bytes).toContain("compact");
|
|
56
|
+
});
|
|
57
|
+
it("no-op when turns ≤ recentFloor + 1", () => {
|
|
58
|
+
const turns = mkTurns(3);
|
|
59
|
+
const result = trimMiddle(turns, { recentFloor: 5 });
|
|
60
|
+
expect(result.droppedCount).toBe(0);
|
|
61
|
+
expect(result.kept).toEqual(turns);
|
|
62
|
+
});
|
|
63
|
+
it("summarize includes dropped indices for auditability", () => {
|
|
64
|
+
const s = summarize([
|
|
65
|
+
{ index: 1, bytes: "a" },
|
|
66
|
+
{ index: 2, bytes: "bb" },
|
|
67
|
+
]);
|
|
68
|
+
expect(s).toContain("[1,2]");
|
|
69
|
+
expect(s).toContain("3 bytes");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("KeepaliveScheduler (T-053, T-054)", () => {
|
|
73
|
+
it("returns skip when retention=none", () => {
|
|
74
|
+
const k = new KeepaliveScheduler();
|
|
75
|
+
k.markActivity(0);
|
|
76
|
+
expect(k.tick(10_000, { intervalMs: 5_000, retention: "none", enabled: true })).toBe("skip");
|
|
77
|
+
});
|
|
78
|
+
it("returns skip when disabled", () => {
|
|
79
|
+
const k = new KeepaliveScheduler();
|
|
80
|
+
k.markActivity(0);
|
|
81
|
+
expect(k.tick(10_000, { intervalMs: 5_000, retention: "long", enabled: false })).toBe("skip");
|
|
82
|
+
});
|
|
83
|
+
it("returns ping after interval elapsed with retention=long", () => {
|
|
84
|
+
const k = new KeepaliveScheduler();
|
|
85
|
+
k.markActivity(0);
|
|
86
|
+
expect(k.tick(5_000, { intervalMs: 5_000, retention: "long", enabled: true })).toBe("ping");
|
|
87
|
+
});
|
|
88
|
+
it("shuts off after idle 2x interval", () => {
|
|
89
|
+
const k = new KeepaliveScheduler();
|
|
90
|
+
k.markActivity(0);
|
|
91
|
+
expect(k.tick(11_000, { intervalMs: 5_000, retention: "long", enabled: true })).toBe("shutoff");
|
|
92
|
+
});
|
|
93
|
+
it("default off: enabled=false skips even when interval passed", () => {
|
|
94
|
+
const k = new KeepaliveScheduler();
|
|
95
|
+
k.markActivity(0);
|
|
96
|
+
const r = k.tick(10_000, { intervalMs: 5_000, retention: "long", enabled: false });
|
|
97
|
+
expect(r).toBe("skip");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=compaction.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.test.js","sourceRoot":"","sources":["../../../src/cache/__tests__/compaction.test.ts"],"names":[],"mappings":"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","sourcesContent":["// T-044..T-054\nimport { describe, expect, it } from \"vitest\";\nimport { KeepaliveScheduler, summarize, type Turn, trimMiddle } from \"../compaction.js\";\nimport { type CacheUsageReport, resolveRetention, totalInputTokens } from \"../policy.js\";\n\ndescribe(\"cache usage report (T-044, T-046)\", () => {\n\tit(\"exposes three token fields\", () => {\n\t\tconst u: CacheUsageReport = {\n\t\t\tcachedInputTokens: 100,\n\t\t\tcacheWriteTokens: 10,\n\t\t\tuncachedInputTokens: 50,\n\t\t};\n\t\texpect(u.cachedInputTokens).toBeDefined();\n\t\texpect(u.cacheWriteTokens).toBeDefined();\n\t\texpect(u.uncachedInputTokens).toBeDefined();\n\t});\n\n\tit(\"cached + uncached equals total input\", () => {\n\t\tconst u: CacheUsageReport = {\n\t\t\tcachedInputTokens: 300,\n\t\t\tcacheWriteTokens: 25,\n\t\t\tuncachedInputTokens: 200,\n\t\t};\n\t\texpect(totalInputTokens(u)).toBe(500);\n\t});\n});\n\ndescribe(\"resolveRetention (T-047, T-048, T-049)\", () => {\n\tit(\"defaults to role default when no override\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"long\" })).toBe(\"long\");\n\t});\n\n\tit(\"CLI flag overrides role default\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"long\", cliFlag: \"none\" })).toBe(\"none\");\n\t});\n});\n\ndescribe(\"trimMiddle (T-050, T-051, T-052)\", () => {\n\tfunction mkTurns(n: number): Turn[] {\n\t\treturn Array.from({ length: n }, (_, i) => ({\n\t\t\tindex: i,\n\t\t\tbytes: `turn-${i}`,\n\t\t}));\n\t}\n\n\tit(\"drops 1..24, preserves 25..30 with N=5 recent floor (30-turn history)\", () => {\n\t\tconst turns = mkTurns(30);\n\t\tconst result = trimMiddle(turns, { recentFloor: 5 });\n\t\texpect(result.kept.length).toBe(6); // summary + 5 recent\n\t\t// Last 5 kept\n\t\tconst recent = result.kept.slice(1);\n\t\texpect(recent.map((t) => t.index)).toEqual([25, 26, 27, 28, 29]);\n\t\t// Dropped 25 turns\n\t\texpect(result.droppedCount).toBe(25);\n\t});\n\n\tit(\"produces deterministic stable summary block\", () => {\n\t\tconst turns = mkTurns(20);\n\t\tconst a = trimMiddle(turns, { recentFloor: 5 }).summary;\n\t\tconst b = trimMiddle(turns, { recentFloor: 5 }).summary;\n\t\texpect(a.bytes).toBe(b.bytes);\n\t\texpect(a.bytes).toContain(\"compact\");\n\t});\n\n\tit(\"no-op when turns ≤ recentFloor + 1\", () => {\n\t\tconst turns = mkTurns(3);\n\t\tconst result = trimMiddle(turns, { recentFloor: 5 });\n\t\texpect(result.droppedCount).toBe(0);\n\t\texpect(result.kept).toEqual(turns);\n\t});\n\n\tit(\"summarize includes dropped indices for auditability\", () => {\n\t\tconst s = summarize([\n\t\t\t{ index: 1, bytes: \"a\" },\n\t\t\t{ index: 2, bytes: \"bb\" },\n\t\t]);\n\t\texpect(s).toContain(\"[1,2]\");\n\t\texpect(s).toContain(\"3 bytes\");\n\t});\n});\n\ndescribe(\"KeepaliveScheduler (T-053, T-054)\", () => {\n\tit(\"returns skip when retention=none\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(10_000, { intervalMs: 5_000, retention: \"none\", enabled: true })).toBe(\"skip\");\n\t});\n\n\tit(\"returns skip when disabled\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(10_000, { intervalMs: 5_000, retention: \"long\", enabled: false })).toBe(\"skip\");\n\t});\n\n\tit(\"returns ping after interval elapsed with retention=long\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(5_000, { intervalMs: 5_000, retention: \"long\", enabled: true })).toBe(\"ping\");\n\t});\n\n\tit(\"shuts off after idle 2x interval\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\texpect(k.tick(11_000, { intervalMs: 5_000, retention: \"long\", enabled: true })).toBe(\"shutoff\");\n\t});\n\n\tit(\"default off: enabled=false skips even when interval passed\", () => {\n\t\tconst k = new KeepaliveScheduler();\n\t\tk.markActivity(0);\n\t\tconst r = k.tick(10_000, { intervalMs: 5_000, retention: \"long\", enabled: false });\n\t\texpect(r).toBe(\"skip\");\n\t});\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.test.d.ts","sourceRoot":"","sources":["../../../src/cache/__tests__/layers.test.ts"],"names":[],"mappings":"","sourcesContent":["// T-002, T-003\nimport { describe, expect, it } from \"vitest\";\nimport { assembleLayered, layerBytes, layerHash, layersInOrder } from \"../layers.js\";\nimport { validateLayers } from \"../policy.js\";\n\nconst tools = [{ name: \"a\", description: \"alpha\", parameters: {} }];\nconst baseInput = {\n\ttools,\n\tsystem: \"you are cave\",\n\tproject: \"CLAUDE.md contents\",\n\tmessages: [\"hi\"],\n\tpolicy: { retention: \"short\" as const, supportsBreakpoints: true },\n};\n\ndescribe(\"assembleLayered\", () => {\n\tit(\"emits layers in exact tools/system/project/messages order\", () => {\n\t\tconst p = assembleLayered(baseInput);\n\t\texpect(layersInOrder(p)).toEqual([\"tools\", \"system\", \"project\", \"messages\"]);\n\t});\n\n\tit(\"non-breakpoint provider accepts same 4-layer payload\", () => {\n\t\tconst p = assembleLayered({\n\t\t\t...baseInput,\n\t\t\tpolicy: { retention: \"short\", supportsBreakpoints: false },\n\t\t});\n\t\texpect(layersInOrder(p)).toEqual([\"tools\", \"system\", \"project\", \"messages\"]);\n\t\tfor (const layer of p.layers) {\n\t\t\texpect(layer.breakpoint).toBe(false);\n\t\t}\n\t});\n\n\tit(\"removing project context does not alter tools/system bytes\", () => {\n\t\tconst withProject = assembleLayered(baseInput);\n\t\tconst withoutProject = assembleLayered({ ...baseInput, project: undefined });\n\t\texpect(layerHash(withProject, \"tools\")).toBe(layerHash(withoutProject, \"tools\"));\n\t\texpect(layerHash(withProject, \"system\")).toBe(layerHash(withoutProject, \"system\"));\n\t\texpect(layersInOrder(withoutProject)).toEqual([\"tools\", \"system\", \"messages\"]);\n\t});\n\n\tit(\"rejects out-of-order layers\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"system\", bytes: \"\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"\" },\n\t\t\t]),\n\t\t).toThrow(/out of canonical order/);\n\t});\n\n\tit(\"rejects duplicate layer\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"\" },\n\t\t\t]),\n\t\t).toThrow(/duplicate layer/);\n\t});\n\n\tit(\"enforces single breakpoint per layer\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"a\", breakpoint: true },\n\t\t\t\t{ layer: \"system\", bytes: \"b\", breakpoint: true },\n\t\t\t\t// Two blocks in the same layer not allowed at all, but validate\n\t\t\t\t// also catches the breakpoint-count path with a hand-crafted list:\n\t\t\t]),\n\t\t).not.toThrow();\n\t});\n\n\tit(\"editing a tool does not change system/project bytes\", () => {\n\t\tconst before = assembleLayered(baseInput);\n\t\tconst after = assembleLayered({\n\t\t\t...baseInput,\n\t\t\ttools: [{ name: \"a\", description: \"alpha edited\", parameters: {} }],\n\t\t});\n\t\texpect(layerHash(before, \"tools\")).not.toBe(layerHash(after, \"tools\"));\n\t\texpect(layerHash(before, \"system\")).toBe(layerHash(after, \"system\"));\n\t\texpect(layerHash(before, \"project\")).toBe(layerHash(after, \"project\"));\n\t});\n\n\tit(\"layer bytes are non-empty string for present layers\", () => {\n\t\tconst p = assembleLayered(baseInput);\n\t\texpect(layerBytes(p, \"tools\").length).toBeGreaterThan(0);\n\t\texpect(layerBytes(p, \"system\")).toBe(\"you are cave\");\n\t});\n});\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// T-002, T-003
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { assembleLayered, layerBytes, layerHash, layersInOrder } from "../layers.js";
|
|
4
|
+
import { validateLayers } from "../policy.js";
|
|
5
|
+
const tools = [{ name: "a", description: "alpha", parameters: {} }];
|
|
6
|
+
const baseInput = {
|
|
7
|
+
tools,
|
|
8
|
+
system: "you are cave",
|
|
9
|
+
project: "CLAUDE.md contents",
|
|
10
|
+
messages: ["hi"],
|
|
11
|
+
policy: { retention: "short", supportsBreakpoints: true },
|
|
12
|
+
};
|
|
13
|
+
describe("assembleLayered", () => {
|
|
14
|
+
it("emits layers in exact tools/system/project/messages order", () => {
|
|
15
|
+
const p = assembleLayered(baseInput);
|
|
16
|
+
expect(layersInOrder(p)).toEqual(["tools", "system", "project", "messages"]);
|
|
17
|
+
});
|
|
18
|
+
it("non-breakpoint provider accepts same 4-layer payload", () => {
|
|
19
|
+
const p = assembleLayered({
|
|
20
|
+
...baseInput,
|
|
21
|
+
policy: { retention: "short", supportsBreakpoints: false },
|
|
22
|
+
});
|
|
23
|
+
expect(layersInOrder(p)).toEqual(["tools", "system", "project", "messages"]);
|
|
24
|
+
for (const layer of p.layers) {
|
|
25
|
+
expect(layer.breakpoint).toBe(false);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
it("removing project context does not alter tools/system bytes", () => {
|
|
29
|
+
const withProject = assembleLayered(baseInput);
|
|
30
|
+
const withoutProject = assembleLayered({ ...baseInput, project: undefined });
|
|
31
|
+
expect(layerHash(withProject, "tools")).toBe(layerHash(withoutProject, "tools"));
|
|
32
|
+
expect(layerHash(withProject, "system")).toBe(layerHash(withoutProject, "system"));
|
|
33
|
+
expect(layersInOrder(withoutProject)).toEqual(["tools", "system", "messages"]);
|
|
34
|
+
});
|
|
35
|
+
it("rejects out-of-order layers", () => {
|
|
36
|
+
expect(() => validateLayers([
|
|
37
|
+
{ layer: "system", bytes: "" },
|
|
38
|
+
{ layer: "tools", bytes: "" },
|
|
39
|
+
])).toThrow(/out of canonical order/);
|
|
40
|
+
});
|
|
41
|
+
it("rejects duplicate layer", () => {
|
|
42
|
+
expect(() => validateLayers([
|
|
43
|
+
{ layer: "tools", bytes: "" },
|
|
44
|
+
{ layer: "tools", bytes: "" },
|
|
45
|
+
])).toThrow(/duplicate layer/);
|
|
46
|
+
});
|
|
47
|
+
it("enforces single breakpoint per layer", () => {
|
|
48
|
+
expect(() => validateLayers([
|
|
49
|
+
{ layer: "tools", bytes: "a", breakpoint: true },
|
|
50
|
+
{ layer: "system", bytes: "b", breakpoint: true },
|
|
51
|
+
// Two blocks in the same layer not allowed at all, but validate
|
|
52
|
+
// also catches the breakpoint-count path with a hand-crafted list:
|
|
53
|
+
])).not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
it("editing a tool does not change system/project bytes", () => {
|
|
56
|
+
const before = assembleLayered(baseInput);
|
|
57
|
+
const after = assembleLayered({
|
|
58
|
+
...baseInput,
|
|
59
|
+
tools: [{ name: "a", description: "alpha edited", parameters: {} }],
|
|
60
|
+
});
|
|
61
|
+
expect(layerHash(before, "tools")).not.toBe(layerHash(after, "tools"));
|
|
62
|
+
expect(layerHash(before, "system")).toBe(layerHash(after, "system"));
|
|
63
|
+
expect(layerHash(before, "project")).toBe(layerHash(after, "project"));
|
|
64
|
+
});
|
|
65
|
+
it("layer bytes are non-empty string for present layers", () => {
|
|
66
|
+
const p = assembleLayered(baseInput);
|
|
67
|
+
expect(layerBytes(p, "tools").length).toBeGreaterThan(0);
|
|
68
|
+
expect(layerBytes(p, "system")).toBe("you are cave");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=layers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layers.test.js","sourceRoot":"","sources":["../../../src/cache/__tests__/layers.test.ts"],"names":[],"mappings":"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","sourcesContent":["// T-002, T-003\nimport { describe, expect, it } from \"vitest\";\nimport { assembleLayered, layerBytes, layerHash, layersInOrder } from \"../layers.js\";\nimport { validateLayers } from \"../policy.js\";\n\nconst tools = [{ name: \"a\", description: \"alpha\", parameters: {} }];\nconst baseInput = {\n\ttools,\n\tsystem: \"you are cave\",\n\tproject: \"CLAUDE.md contents\",\n\tmessages: [\"hi\"],\n\tpolicy: { retention: \"short\" as const, supportsBreakpoints: true },\n};\n\ndescribe(\"assembleLayered\", () => {\n\tit(\"emits layers in exact tools/system/project/messages order\", () => {\n\t\tconst p = assembleLayered(baseInput);\n\t\texpect(layersInOrder(p)).toEqual([\"tools\", \"system\", \"project\", \"messages\"]);\n\t});\n\n\tit(\"non-breakpoint provider accepts same 4-layer payload\", () => {\n\t\tconst p = assembleLayered({\n\t\t\t...baseInput,\n\t\t\tpolicy: { retention: \"short\", supportsBreakpoints: false },\n\t\t});\n\t\texpect(layersInOrder(p)).toEqual([\"tools\", \"system\", \"project\", \"messages\"]);\n\t\tfor (const layer of p.layers) {\n\t\t\texpect(layer.breakpoint).toBe(false);\n\t\t}\n\t});\n\n\tit(\"removing project context does not alter tools/system bytes\", () => {\n\t\tconst withProject = assembleLayered(baseInput);\n\t\tconst withoutProject = assembleLayered({ ...baseInput, project: undefined });\n\t\texpect(layerHash(withProject, \"tools\")).toBe(layerHash(withoutProject, \"tools\"));\n\t\texpect(layerHash(withProject, \"system\")).toBe(layerHash(withoutProject, \"system\"));\n\t\texpect(layersInOrder(withoutProject)).toEqual([\"tools\", \"system\", \"messages\"]);\n\t});\n\n\tit(\"rejects out-of-order layers\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"system\", bytes: \"\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"\" },\n\t\t\t]),\n\t\t).toThrow(/out of canonical order/);\n\t});\n\n\tit(\"rejects duplicate layer\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"\" },\n\t\t\t]),\n\t\t).toThrow(/duplicate layer/);\n\t});\n\n\tit(\"enforces single breakpoint per layer\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"a\", breakpoint: true },\n\t\t\t\t{ layer: \"system\", bytes: \"b\", breakpoint: true },\n\t\t\t\t// Two blocks in the same layer not allowed at all, but validate\n\t\t\t\t// also catches the breakpoint-count path with a hand-crafted list:\n\t\t\t]),\n\t\t).not.toThrow();\n\t});\n\n\tit(\"editing a tool does not change system/project bytes\", () => {\n\t\tconst before = assembleLayered(baseInput);\n\t\tconst after = assembleLayered({\n\t\t\t...baseInput,\n\t\t\ttools: [{ name: \"a\", description: \"alpha edited\", parameters: {} }],\n\t\t});\n\t\texpect(layerHash(before, \"tools\")).not.toBe(layerHash(after, \"tools\"));\n\t\texpect(layerHash(before, \"system\")).toBe(layerHash(after, \"system\"));\n\t\texpect(layerHash(before, \"project\")).toBe(layerHash(after, \"project\"));\n\t});\n\n\tit(\"layer bytes are non-empty string for present layers\", () => {\n\t\tconst p = assembleLayered(baseInput);\n\t\texpect(layerBytes(p, \"tools\").length).toBeGreaterThan(0);\n\t\texpect(layerBytes(p, \"system\")).toBe(\"you are cave\");\n\t});\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.test.d.ts","sourceRoot":"","sources":["../../../src/cache/__tests__/policy.test.ts"],"names":[],"mappings":"","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { defaultPolicy, resolveRetention, totalInputTokens, usageToCacheReport, validateLayers } from \"../policy.js\";\n\ndescribe(\"usageToCacheReport\", () => {\n\tit(\"converts Usage to CacheUsageReport\", () => {\n\t\tconst report = usageToCacheReport({\n\t\t\tinput: 500,\n\t\t\toutput: 200,\n\t\t\tcacheRead: 300,\n\t\t\tcacheWrite: 100,\n\t\t\ttotalTokens: 1100,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t});\n\t\texpect(report.cachedInputTokens).toBe(300);\n\t\texpect(report.cacheWriteTokens).toBe(100);\n\t\texpect(report.uncachedInputTokens).toBe(500);\n\t\texpect(totalInputTokens(report)).toBe(800);\n\t});\n\n\tit(\"clamps negative input to zero\", () => {\n\t\tconst report = usageToCacheReport({\n\t\t\tinput: -10,\n\t\t\toutput: 50,\n\t\t\tcacheRead: 100,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 140,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t});\n\t\texpect(report.uncachedInputTokens).toBe(0);\n\t});\n\n\tit(\"handles zero-cache usage (e.g. Mistral)\", () => {\n\t\tconst report = usageToCacheReport({\n\t\t\tinput: 1000,\n\t\t\toutput: 200,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 1200,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t});\n\t\texpect(report.cachedInputTokens).toBe(0);\n\t\texpect(report.cacheWriteTokens).toBe(0);\n\t\texpect(report.uncachedInputTokens).toBe(1000);\n\t\texpect(totalInputTokens(report)).toBe(1000);\n\t});\n});\n\ndescribe(\"resolveRetention\", () => {\n\tit(\"returns cliFlag when present\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"short\", cliFlag: \"long\" })).toBe(\"long\");\n\t});\n\n\tit(\"returns roleDefault as fallback\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"long\" })).toBe(\"long\");\n\t});\n});\n\ndescribe(\"validateLayers\", () => {\n\tit(\"accepts valid layers in canonical order\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"t\" },\n\t\t\t\t{ layer: \"system\", bytes: \"s\" },\n\t\t\t]),\n\t\t).not.toThrow();\n\t});\n\n\tit(\"rejects duplicate layers\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"a\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"b\" },\n\t\t\t]),\n\t\t).toThrow(/duplicate layer/);\n\t});\n\n\tit(\"rejects out-of-order layers\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"system\", bytes: \"s\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"t\" },\n\t\t\t]),\n\t\t).toThrow(/out of canonical order/);\n\t});\n});\n\ndescribe(\"defaultPolicy\", () => {\n\tit(\"returns short retention with no breakpoints\", () => {\n\t\tconst p = defaultPolicy();\n\t\texpect(p.retention).toBe(\"short\");\n\t\texpect(p.supportsBreakpoints).toBe(false);\n\t});\n});\n"]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { defaultPolicy, resolveRetention, totalInputTokens, usageToCacheReport, validateLayers } from "../policy.js";
|
|
3
|
+
describe("usageToCacheReport", () => {
|
|
4
|
+
it("converts Usage to CacheUsageReport", () => {
|
|
5
|
+
const report = usageToCacheReport({
|
|
6
|
+
input: 500,
|
|
7
|
+
output: 200,
|
|
8
|
+
cacheRead: 300,
|
|
9
|
+
cacheWrite: 100,
|
|
10
|
+
totalTokens: 1100,
|
|
11
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
12
|
+
});
|
|
13
|
+
expect(report.cachedInputTokens).toBe(300);
|
|
14
|
+
expect(report.cacheWriteTokens).toBe(100);
|
|
15
|
+
expect(report.uncachedInputTokens).toBe(500);
|
|
16
|
+
expect(totalInputTokens(report)).toBe(800);
|
|
17
|
+
});
|
|
18
|
+
it("clamps negative input to zero", () => {
|
|
19
|
+
const report = usageToCacheReport({
|
|
20
|
+
input: -10,
|
|
21
|
+
output: 50,
|
|
22
|
+
cacheRead: 100,
|
|
23
|
+
cacheWrite: 0,
|
|
24
|
+
totalTokens: 140,
|
|
25
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
26
|
+
});
|
|
27
|
+
expect(report.uncachedInputTokens).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
it("handles zero-cache usage (e.g. Mistral)", () => {
|
|
30
|
+
const report = usageToCacheReport({
|
|
31
|
+
input: 1000,
|
|
32
|
+
output: 200,
|
|
33
|
+
cacheRead: 0,
|
|
34
|
+
cacheWrite: 0,
|
|
35
|
+
totalTokens: 1200,
|
|
36
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
37
|
+
});
|
|
38
|
+
expect(report.cachedInputTokens).toBe(0);
|
|
39
|
+
expect(report.cacheWriteTokens).toBe(0);
|
|
40
|
+
expect(report.uncachedInputTokens).toBe(1000);
|
|
41
|
+
expect(totalInputTokens(report)).toBe(1000);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("resolveRetention", () => {
|
|
45
|
+
it("returns cliFlag when present", () => {
|
|
46
|
+
expect(resolveRetention({ roleDefault: "short", cliFlag: "long" })).toBe("long");
|
|
47
|
+
});
|
|
48
|
+
it("returns roleDefault as fallback", () => {
|
|
49
|
+
expect(resolveRetention({ roleDefault: "long" })).toBe("long");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("validateLayers", () => {
|
|
53
|
+
it("accepts valid layers in canonical order", () => {
|
|
54
|
+
expect(() => validateLayers([
|
|
55
|
+
{ layer: "tools", bytes: "t" },
|
|
56
|
+
{ layer: "system", bytes: "s" },
|
|
57
|
+
])).not.toThrow();
|
|
58
|
+
});
|
|
59
|
+
it("rejects duplicate layers", () => {
|
|
60
|
+
expect(() => validateLayers([
|
|
61
|
+
{ layer: "tools", bytes: "a" },
|
|
62
|
+
{ layer: "tools", bytes: "b" },
|
|
63
|
+
])).toThrow(/duplicate layer/);
|
|
64
|
+
});
|
|
65
|
+
it("rejects out-of-order layers", () => {
|
|
66
|
+
expect(() => validateLayers([
|
|
67
|
+
{ layer: "system", bytes: "s" },
|
|
68
|
+
{ layer: "tools", bytes: "t" },
|
|
69
|
+
])).toThrow(/out of canonical order/);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("defaultPolicy", () => {
|
|
73
|
+
it("returns short retention with no breakpoints", () => {
|
|
74
|
+
const p = defaultPolicy();
|
|
75
|
+
expect(p.retention).toBe("short");
|
|
76
|
+
expect(p.supportsBreakpoints).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=policy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.test.js","sourceRoot":"","sources":["../../../src/cache/__tests__/policy.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport { defaultPolicy, resolveRetention, totalInputTokens, usageToCacheReport, validateLayers } from \"../policy.js\";\n\ndescribe(\"usageToCacheReport\", () => {\n\tit(\"converts Usage to CacheUsageReport\", () => {\n\t\tconst report = usageToCacheReport({\n\t\t\tinput: 500,\n\t\t\toutput: 200,\n\t\t\tcacheRead: 300,\n\t\t\tcacheWrite: 100,\n\t\t\ttotalTokens: 1100,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t});\n\t\texpect(report.cachedInputTokens).toBe(300);\n\t\texpect(report.cacheWriteTokens).toBe(100);\n\t\texpect(report.uncachedInputTokens).toBe(500);\n\t\texpect(totalInputTokens(report)).toBe(800);\n\t});\n\n\tit(\"clamps negative input to zero\", () => {\n\t\tconst report = usageToCacheReport({\n\t\t\tinput: -10,\n\t\t\toutput: 50,\n\t\t\tcacheRead: 100,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 140,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t});\n\t\texpect(report.uncachedInputTokens).toBe(0);\n\t});\n\n\tit(\"handles zero-cache usage (e.g. Mistral)\", () => {\n\t\tconst report = usageToCacheReport({\n\t\t\tinput: 1000,\n\t\t\toutput: 200,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 1200,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t});\n\t\texpect(report.cachedInputTokens).toBe(0);\n\t\texpect(report.cacheWriteTokens).toBe(0);\n\t\texpect(report.uncachedInputTokens).toBe(1000);\n\t\texpect(totalInputTokens(report)).toBe(1000);\n\t});\n});\n\ndescribe(\"resolveRetention\", () => {\n\tit(\"returns cliFlag when present\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"short\", cliFlag: \"long\" })).toBe(\"long\");\n\t});\n\n\tit(\"returns roleDefault as fallback\", () => {\n\t\texpect(resolveRetention({ roleDefault: \"long\" })).toBe(\"long\");\n\t});\n});\n\ndescribe(\"validateLayers\", () => {\n\tit(\"accepts valid layers in canonical order\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"t\" },\n\t\t\t\t{ layer: \"system\", bytes: \"s\" },\n\t\t\t]),\n\t\t).not.toThrow();\n\t});\n\n\tit(\"rejects duplicate layers\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"tools\", bytes: \"a\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"b\" },\n\t\t\t]),\n\t\t).toThrow(/duplicate layer/);\n\t});\n\n\tit(\"rejects out-of-order layers\", () => {\n\t\texpect(() =>\n\t\t\tvalidateLayers([\n\t\t\t\t{ layer: \"system\", bytes: \"s\" },\n\t\t\t\t{ layer: \"tools\", bytes: \"t\" },\n\t\t\t]),\n\t\t).toThrow(/out of canonical order/);\n\t});\n});\n\ndescribe(\"defaultPolicy\", () => {\n\tit(\"returns short retention with no breakpoints\", () => {\n\t\tconst p = defaultPolicy();\n\t\texpect(p.retention).toBe(\"short\");\n\t\texpect(p.supportsBreakpoints).toBe(false);\n\t});\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-serializer.test.d.ts","sourceRoot":"","sources":["../../../src/cache/__tests__/tool-serializer.test.ts"],"names":[],"mappings":"","sourcesContent":["// T-005, T-006\nimport { describe, expect, it } from \"vitest\";\nimport { serializeToolSchemas, toolSchemaHash } from \"../tool-serializer.js\";\n\nconst baseTools = [\n\t{\n\t\tname: \"bash\",\n\t\tdescription: \"Run a shell command\",\n\t\tparameters: { type: \"object\", properties: { cmd: { type: \"string\" } } },\n\t},\n\t{\n\t\tname: \"read\",\n\t\tdescription: \"Read a file\",\n\t\tparameters: { type: \"object\", properties: { path: { type: \"string\" } } },\n\t},\n\t{\n\t\tname: \"apply_sr_diff\",\n\t\tdescription: \"Exact-match search/replace edit\",\n\t\tparameters: { type: \"object\", properties: { file: { type: \"string\" } } },\n\t},\n];\n\ndescribe(\"serializeToolSchemas\", () => {\n\tit(\"produces byte-stable SHA256 across 1000 invocations\", () => {\n\t\tconst first = toolSchemaHash(baseTools);\n\t\tfor (let i = 0; i < 1000; i++) {\n\t\t\texpect(toolSchemaHash(baseTools)).toBe(first);\n\t\t}\n\t});\n\n\tit(\"reordering tools at call site does not change bytes\", () => {\n\t\tconst a = serializeToolSchemas(baseTools);\n\t\tconst shuffled = [baseTools[2], baseTools[0], baseTools[1]];\n\t\tconst b = serializeToolSchemas(shuffled);\n\t\texpect(a).toBe(b);\n\t});\n\n\tit(\"editing one tool description isolates to tools-layer hash (it changes)\", () => {\n\t\tconst original = toolSchemaHash(baseTools);\n\t\tconst edited = baseTools.map((t) => (t.name === \"read\" ? { ...t, description: \"Read a file from disk\" } : t));\n\t\tconst editedHash = toolSchemaHash(edited);\n\t\texpect(editedHash).not.toBe(original);\n\t});\n\n\tit(\"strips absolute paths and ISO timestamps from descriptions\", () => {\n\t\tconst tools = [\n\t\t\t{\n\t\t\t\tname: \"log\",\n\t\t\t\tdescription: \"Written at 2025-03-15T12:34:56Z from /Users/alice/project\",\n\t\t\t\tparameters: {},\n\t\t\t},\n\t\t];\n\t\tconst json = serializeToolSchemas(tools);\n\t\texpect(json).not.toContain(\"2025-03-15\");\n\t\texpect(json).not.toContain(\"/Users/alice\");\n\t\texpect(json).toContain(\"<ts>\");\n\t\texpect(json).toContain(\"<path>\");\n\t});\n\n\tit(\"sorts object keys deeply for deterministic output\", () => {\n\t\tconst a = serializeToolSchemas([\n\t\t\t{ name: \"t\", description: \"d\", parameters: { b: 1, a: 2, nested: { z: 1, y: 2 } } },\n\t\t]);\n\t\tconst b = serializeToolSchemas([\n\t\t\t{ name: \"t\", description: \"d\", parameters: { nested: { y: 2, z: 1 }, a: 2, b: 1 } },\n\t\t]);\n\t\texpect(a).toBe(b);\n\t});\n});\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// T-005, T-006
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { serializeToolSchemas, toolSchemaHash } from "../tool-serializer.js";
|
|
4
|
+
const baseTools = [
|
|
5
|
+
{
|
|
6
|
+
name: "bash",
|
|
7
|
+
description: "Run a shell command",
|
|
8
|
+
parameters: { type: "object", properties: { cmd: { type: "string" } } },
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "read",
|
|
12
|
+
description: "Read a file",
|
|
13
|
+
parameters: { type: "object", properties: { path: { type: "string" } } },
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "apply_sr_diff",
|
|
17
|
+
description: "Exact-match search/replace edit",
|
|
18
|
+
parameters: { type: "object", properties: { file: { type: "string" } } },
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
describe("serializeToolSchemas", () => {
|
|
22
|
+
it("produces byte-stable SHA256 across 1000 invocations", () => {
|
|
23
|
+
const first = toolSchemaHash(baseTools);
|
|
24
|
+
for (let i = 0; i < 1000; i++) {
|
|
25
|
+
expect(toolSchemaHash(baseTools)).toBe(first);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
it("reordering tools at call site does not change bytes", () => {
|
|
29
|
+
const a = serializeToolSchemas(baseTools);
|
|
30
|
+
const shuffled = [baseTools[2], baseTools[0], baseTools[1]];
|
|
31
|
+
const b = serializeToolSchemas(shuffled);
|
|
32
|
+
expect(a).toBe(b);
|
|
33
|
+
});
|
|
34
|
+
it("editing one tool description isolates to tools-layer hash (it changes)", () => {
|
|
35
|
+
const original = toolSchemaHash(baseTools);
|
|
36
|
+
const edited = baseTools.map((t) => (t.name === "read" ? { ...t, description: "Read a file from disk" } : t));
|
|
37
|
+
const editedHash = toolSchemaHash(edited);
|
|
38
|
+
expect(editedHash).not.toBe(original);
|
|
39
|
+
});
|
|
40
|
+
it("strips absolute paths and ISO timestamps from descriptions", () => {
|
|
41
|
+
const tools = [
|
|
42
|
+
{
|
|
43
|
+
name: "log",
|
|
44
|
+
description: "Written at 2025-03-15T12:34:56Z from /Users/alice/project",
|
|
45
|
+
parameters: {},
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
const json = serializeToolSchemas(tools);
|
|
49
|
+
expect(json).not.toContain("2025-03-15");
|
|
50
|
+
expect(json).not.toContain("/Users/alice");
|
|
51
|
+
expect(json).toContain("<ts>");
|
|
52
|
+
expect(json).toContain("<path>");
|
|
53
|
+
});
|
|
54
|
+
it("sorts object keys deeply for deterministic output", () => {
|
|
55
|
+
const a = serializeToolSchemas([
|
|
56
|
+
{ name: "t", description: "d", parameters: { b: 1, a: 2, nested: { z: 1, y: 2 } } },
|
|
57
|
+
]);
|
|
58
|
+
const b = serializeToolSchemas([
|
|
59
|
+
{ name: "t", description: "d", parameters: { nested: { y: 2, z: 1 }, a: 2, b: 1 } },
|
|
60
|
+
]);
|
|
61
|
+
expect(a).toBe(b);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=tool-serializer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-serializer.test.js","sourceRoot":"","sources":["../../../src/cache/__tests__/tool-serializer.test.ts"],"names":[],"mappings":"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","sourcesContent":["// T-005, T-006\nimport { describe, expect, it } from \"vitest\";\nimport { serializeToolSchemas, toolSchemaHash } from \"../tool-serializer.js\";\n\nconst baseTools = [\n\t{\n\t\tname: \"bash\",\n\t\tdescription: \"Run a shell command\",\n\t\tparameters: { type: \"object\", properties: { cmd: { type: \"string\" } } },\n\t},\n\t{\n\t\tname: \"read\",\n\t\tdescription: \"Read a file\",\n\t\tparameters: { type: \"object\", properties: { path: { type: \"string\" } } },\n\t},\n\t{\n\t\tname: \"apply_sr_diff\",\n\t\tdescription: \"Exact-match search/replace edit\",\n\t\tparameters: { type: \"object\", properties: { file: { type: \"string\" } } },\n\t},\n];\n\ndescribe(\"serializeToolSchemas\", () => {\n\tit(\"produces byte-stable SHA256 across 1000 invocations\", () => {\n\t\tconst first = toolSchemaHash(baseTools);\n\t\tfor (let i = 0; i < 1000; i++) {\n\t\t\texpect(toolSchemaHash(baseTools)).toBe(first);\n\t\t}\n\t});\n\n\tit(\"reordering tools at call site does not change bytes\", () => {\n\t\tconst a = serializeToolSchemas(baseTools);\n\t\tconst shuffled = [baseTools[2], baseTools[0], baseTools[1]];\n\t\tconst b = serializeToolSchemas(shuffled);\n\t\texpect(a).toBe(b);\n\t});\n\n\tit(\"editing one tool description isolates to tools-layer hash (it changes)\", () => {\n\t\tconst original = toolSchemaHash(baseTools);\n\t\tconst edited = baseTools.map((t) => (t.name === \"read\" ? { ...t, description: \"Read a file from disk\" } : t));\n\t\tconst editedHash = toolSchemaHash(edited);\n\t\texpect(editedHash).not.toBe(original);\n\t});\n\n\tit(\"strips absolute paths and ISO timestamps from descriptions\", () => {\n\t\tconst tools = [\n\t\t\t{\n\t\t\t\tname: \"log\",\n\t\t\t\tdescription: \"Written at 2025-03-15T12:34:56Z from /Users/alice/project\",\n\t\t\t\tparameters: {},\n\t\t\t},\n\t\t];\n\t\tconst json = serializeToolSchemas(tools);\n\t\texpect(json).not.toContain(\"2025-03-15\");\n\t\texpect(json).not.toContain(\"/Users/alice\");\n\t\texpect(json).toContain(\"<ts>\");\n\t\texpect(json).toContain(\"<path>\");\n\t});\n\n\tit(\"sorts object keys deeply for deterministic output\", () => {\n\t\tconst a = serializeToolSchemas([\n\t\t\t{ name: \"t\", description: \"d\", parameters: { b: 1, a: 2, nested: { z: 1, y: 2 } } },\n\t\t]);\n\t\tconst b = serializeToolSchemas([\n\t\t\t{ name: \"t\", description: \"d\", parameters: { nested: { y: 2, z: 1 }, a: 2, b: 1 } },\n\t\t]);\n\t\texpect(a).toBe(b);\n\t});\n});\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface Turn {
|
|
2
|
+
index: number;
|
|
3
|
+
bytes: string;
|
|
4
|
+
}
|
|
5
|
+
export interface TrimConfig {
|
|
6
|
+
/** Keep the most recent N turns untouched. */
|
|
7
|
+
recentFloor: number;
|
|
8
|
+
}
|
|
9
|
+
export interface TrimResult {
|
|
10
|
+
kept: Turn[];
|
|
11
|
+
summary: Turn;
|
|
12
|
+
droppedCount: number;
|
|
13
|
+
}
|
|
14
|
+
/** Deterministic summary bytes derived from dropped turns. */
|
|
15
|
+
export declare function summarize(dropped: Turn[]): string;
|
|
16
|
+
/** Middle-drop: keep [0..pivot] as a placeholder floor and [tail - recentFloor..tail];
|
|
17
|
+
* drop the middle span and replace with a stable summary. */
|
|
18
|
+
export declare function trimMiddle(turns: Turn[], config: TrimConfig): TrimResult;
|
|
19
|
+
export interface KeepaliveConfig {
|
|
20
|
+
intervalMs: number;
|
|
21
|
+
retention: "long" | "short" | "none";
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare class KeepaliveScheduler {
|
|
25
|
+
private lastPingMs;
|
|
26
|
+
private lastActivityMs;
|
|
27
|
+
tick(now: number, config: KeepaliveConfig): "ping" | "shutoff" | "skip";
|
|
28
|
+
markActivity(now: number): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=compaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/cache/compaction.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,IAAI;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IAC1B,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,OAAO,EAAE,IAAI,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAIjD;AAED;8DAC8D;AAC9D,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAmBxE;AAGD,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACrC,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,cAAc,CAAK;IAE3B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAQtE;IAED,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAG9B;CACD","sourcesContent":["// T-050, T-051, T-052: cache-aware middle-drop trimming with N-recent-turn floor\n// + deterministic stable summary block.\n\nexport interface Turn {\n\tindex: number;\n\tbytes: string;\n}\n\nexport interface TrimConfig {\n\t/** Keep the most recent N turns untouched. */\n\trecentFloor: number;\n}\n\nexport interface TrimResult {\n\tkept: Turn[];\n\tsummary: Turn;\n\tdroppedCount: number;\n}\n\n/** Deterministic summary bytes derived from dropped turns. */\nexport function summarize(dropped: Turn[]): string {\n\tconst indices = dropped.map((t) => t.index).join(\",\");\n\tconst totalBytes = dropped.reduce((acc, t) => acc + t.bytes.length, 0);\n\treturn `<compact ${dropped.length} turns [${indices}], ${totalBytes} bytes>`;\n}\n\n/** Middle-drop: keep [0..pivot] as a placeholder floor and [tail - recentFloor..tail];\n * drop the middle span and replace with a stable summary. */\nexport function trimMiddle(turns: Turn[], config: TrimConfig): TrimResult {\n\tif (turns.length <= config.recentFloor + 1) {\n\t\treturn {\n\t\t\tkept: turns,\n\t\t\tsummary: { index: -1, bytes: \"\" },\n\t\t\tdroppedCount: 0,\n\t\t};\n\t}\n\tconst tail = turns.slice(turns.length - config.recentFloor);\n\tconst middle = turns.slice(0, turns.length - config.recentFloor);\n\tconst summary: Turn = {\n\t\tindex: middle[middle.length - 1].index + 0.5,\n\t\tbytes: summarize(middle),\n\t};\n\treturn {\n\t\tkept: [summary, ...tail],\n\t\tsummary,\n\t\tdroppedCount: middle.length,\n\t};\n}\n\n// T-053, T-054: opt-in keepalive pings with idle shutoff.\nexport interface KeepaliveConfig {\n\tintervalMs: number;\n\tretention: \"long\" | \"short\" | \"none\";\n\tenabled: boolean;\n}\n\nexport class KeepaliveScheduler {\n\tprivate lastPingMs = 0;\n\tprivate lastActivityMs = 0;\n\n\ttick(now: number, config: KeepaliveConfig): \"ping\" | \"shutoff\" | \"skip\" {\n\t\tif (!config.enabled || config.retention === \"none\") return \"skip\";\n\t\tif (now - this.lastActivityMs > config.intervalMs * 2) return \"shutoff\";\n\t\tif (now - this.lastPingMs >= config.intervalMs) {\n\t\t\tthis.lastPingMs = now;\n\t\t\treturn \"ping\";\n\t\t}\n\t\treturn \"skip\";\n\t}\n\n\tmarkActivity(now: number): void {\n\t\tthis.lastActivityMs = now;\n\t\tthis.lastPingMs = now;\n\t}\n}\n"]}
|