bazis 0.138.0 → 0.140.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.
- package/README.md +13 -5
- package/core/access/Access.module.ts +6 -1
- package/core/access/catalog/AccessCatalog.ts +56 -7
- package/core/access/catalog/IAccessCatalog.service.ts +28 -2
- package/core/access/catalog/PermissionSourceLoader.ts +43 -0
- package/core/access/catalog/PermissionSources.ts +222 -0
- package/core/access/catalog/moduleFields.ts +60 -6
- package/core/access/catalog/permissions.ts +46 -1
- package/core/access/check/Access.ts +55 -28
- package/core/access/check/accessConfig.ts +9 -1
- package/core/access/check/can.ts +2 -2
- package/core/access/check/conditions.ts +69 -11
- package/core/access/check/decimal.ts +61 -0
- package/core/access/check/filter.ts +39 -8
- package/core/access/contracts/PermissionSource.ts +32 -0
- package/core/access/contracts/types.ts +12 -2
- package/core/access/generated/deps.ts +5 -1
- package/core/access/http/AccessCatalog.controller.ts +9 -2
- package/core/access/index.ts +12 -1
- package/core/agent/AgentContextBuilder.ts +119 -16
- package/core/agent/AgentRuntime.ts +8 -0
- package/core/agent/AgentToolExecutor.ts +7 -0
- package/core/agent/ModelProvider.ts +6 -0
- package/core/agent/README.md +35 -3
- package/core/agent/errors.ts +32 -0
- package/core/agent/index.ts +7 -0
- package/core/agent/internal/AgentExecutionDriver.ts +598 -24
- package/core/agent/internal/AgentExecutionState.ts +26 -3
- package/core/agent/semantic.ts +60 -1
- package/core/cache/SPEC.md +3 -3
- package/core/di/README.md +10 -3
- package/core/infra/anthropic.ts +11 -0
- package/core/infra/connectors/anthropic.ts +440 -0
- package/core/infra/connectors/codex/CodexAppServerClient.ts +1 -1
- package/core/infra/connectors/llm.ts +12 -0
- package/core/infra/connectors/llmRouter.ts +12 -0
- package/core/infra/connectors/openaiCompatible.ts +128 -63
- package/core/infra/connectors/openaiTextStream.ts +5 -2
- package/core/infra/connectors/toolNames.ts +49 -0
- package/core/scripts/di-generate.ts +34 -4
- package/core/websocket/DESIGN.md +2 -2
- package/index.ts +5 -0
- package/library/orm/Repository/README.md +14 -11
- package/package.json +7 -2
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
agentData,
|
|
10
10
|
agentMessage,
|
|
11
11
|
agentModelResponse,
|
|
12
|
+
agentReasoning,
|
|
12
13
|
agentText,
|
|
13
14
|
agentToolCall,
|
|
14
15
|
agentToolCallPart,
|
|
@@ -340,13 +341,21 @@ function parseResponse(value: BoundaryJsonValue): AgentExecutionResponse {
|
|
|
340
341
|
}
|
|
341
342
|
|
|
342
343
|
function parseUsage(value: BoundaryJsonValue): NonNullable<AgentModelResponse["usage"]> {
|
|
343
|
-
|
|
344
|
+
// Checkpoints written before the cache and reasoning counts have six fields.
|
|
345
|
+
const extended = Object.prototype.hasOwnProperty.call(object(value), "cacheReadTokens");
|
|
346
|
+
const record: { readonly [Key in "inputTokens" | "outputTokens" | "totalTokens" | "latencyMs" | "cost" | "cacheHit"]: BoundaryJsonValue } & { readonly [Key in "cacheReadTokens" | "cacheWriteTokens" | "reasoningTokens"]?: BoundaryJsonValue } = extended
|
|
347
|
+
? closed(value, ["inputTokens", "outputTokens", "totalTokens", "cacheReadTokens", "cacheWriteTokens", "reasoningTokens", "latencyMs", "cost", "cacheHit"])
|
|
348
|
+
: closed(value, ["inputTokens", "outputTokens", "totalTokens", "latencyMs", "cost", "cacheHit"]);
|
|
344
349
|
if (record.cacheHit !== null && typeof record.cacheHit !== "boolean") throw invalid();
|
|
350
|
+
const optionalCount = (count: BoundaryJsonValue | undefined): number | undefined => count === undefined || count === null ? undefined : nonNegativeInteger(count);
|
|
345
351
|
const cost = record.cost === null ? undefined : parseCost(record.cost);
|
|
346
352
|
return freezeWithoutUndefined({
|
|
347
353
|
inputTokens: record.inputTokens === null ? undefined : nonNegativeInteger(record.inputTokens),
|
|
348
354
|
outputTokens: record.outputTokens === null ? undefined : nonNegativeInteger(record.outputTokens),
|
|
349
355
|
totalTokens: record.totalTokens === null ? undefined : nonNegativeInteger(record.totalTokens),
|
|
356
|
+
cacheReadTokens: optionalCount(record.cacheReadTokens),
|
|
357
|
+
cacheWriteTokens: optionalCount(record.cacheWriteTokens),
|
|
358
|
+
reasoningTokens: optionalCount(record.reasoningTokens),
|
|
350
359
|
latencyMs: record.latencyMs === null ? undefined : nonNegativeInteger(record.latencyMs),
|
|
351
360
|
cost,
|
|
352
361
|
cacheHit: record.cacheHit === null ? undefined : record.cacheHit,
|
|
@@ -385,6 +394,11 @@ function parseContentPart(value: BoundaryJsonValue): AgentContentPart {
|
|
|
385
394
|
const call = closed(record, ["kind", "call"]);
|
|
386
395
|
return agentToolCallPart(parseToolCall(call.call));
|
|
387
396
|
}
|
|
397
|
+
if (record.kind === "reasoning") {
|
|
398
|
+
const reasoning = closed(record, ["kind", "text", "data"]);
|
|
399
|
+
if (typeof reasoning.text !== "string") throw invalid();
|
|
400
|
+
return agentReasoning(reasoning.text, { data: reasoning.data === null ? undefined : asJsonObject(reasoning.data) });
|
|
401
|
+
}
|
|
388
402
|
if (record.kind === "tool-result") {
|
|
389
403
|
const result = closed(record, ["kind", "result"]);
|
|
390
404
|
return agentToolResultPart(parseToolResult(result.result));
|
|
@@ -393,11 +407,16 @@ function parseContentPart(value: BoundaryJsonValue): AgentContentPart {
|
|
|
393
407
|
}
|
|
394
408
|
|
|
395
409
|
function parseToolCall(value: BoundaryJsonValue): AgentToolCall {
|
|
396
|
-
|
|
410
|
+
// `inputError` is written only for a call whose arguments could not be read.
|
|
411
|
+
const withError = Object.prototype.hasOwnProperty.call(object(value), "inputError");
|
|
412
|
+
const record: { readonly id: BoundaryJsonValue; readonly name: BoundaryJsonValue; readonly input: BoundaryJsonValue; readonly metadata: BoundaryJsonValue; readonly inputError?: BoundaryJsonValue } = withError
|
|
413
|
+
? closed(value, ["id", "name", "input", "inputError", "metadata"])
|
|
414
|
+
: closed(value, ["id", "name", "input", "metadata"]);
|
|
397
415
|
return agentToolCall({
|
|
398
416
|
id: requiredString(record.id),
|
|
399
417
|
name: requiredString(record.name),
|
|
400
418
|
input: asJsonValue(record.input),
|
|
419
|
+
...(record.inputError === undefined ? {} : { inputError: requiredString(record.inputError) }),
|
|
401
420
|
metadata: asJsonObject(record.metadata),
|
|
402
421
|
});
|
|
403
422
|
}
|
|
@@ -631,6 +650,9 @@ function responseRecord(response: AgentExecutionResponse): Record<string, unknow
|
|
|
631
650
|
inputTokens: response.usage.inputTokens ?? null,
|
|
632
651
|
outputTokens: response.usage.outputTokens ?? null,
|
|
633
652
|
totalTokens: response.usage.totalTokens ?? null,
|
|
653
|
+
cacheReadTokens: response.usage.cacheReadTokens ?? null,
|
|
654
|
+
cacheWriteTokens: response.usage.cacheWriteTokens ?? null,
|
|
655
|
+
reasoningTokens: response.usage.reasoningTokens ?? null,
|
|
634
656
|
latencyMs: response.usage.latencyMs ?? null,
|
|
635
657
|
cost: response.usage.cost ?? null,
|
|
636
658
|
cacheHit: response.usage.cacheHit ?? null,
|
|
@@ -655,12 +677,13 @@ function contentPartRecord(part: AgentContentPart): Record<string, unknown> {
|
|
|
655
677
|
if (part.kind === "text") return { kind: "text", text: part.text };
|
|
656
678
|
if (part.kind === "data") return { kind: "data", value: part.value, name: part.name ?? null };
|
|
657
679
|
if (part.kind === "tool-call") return { kind: "tool-call", call: toolCallRecord(part.call) };
|
|
680
|
+
if (part.kind === "reasoning") return { kind: "reasoning", text: part.text, data: part.data ?? null };
|
|
658
681
|
if (part.kind === "tool-result") return { kind: "tool-result", result: toolResultRecord(part.result) };
|
|
659
682
|
throw invalid();
|
|
660
683
|
}
|
|
661
684
|
|
|
662
685
|
function toolCallRecord(call: AgentToolCall): Record<string, unknown> {
|
|
663
|
-
return { id: call.id, name: call.name, input: call.input, metadata: call.metadata };
|
|
686
|
+
return { id: call.id, name: call.name, input: call.input, ...(call.inputError === undefined ? {} : { inputError: call.inputError }), metadata: call.metadata };
|
|
664
687
|
}
|
|
665
688
|
|
|
666
689
|
function toolResultRecord(result: AgentToolResult): Record<string, unknown> {
|
package/core/agent/semantic.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type AgentContentPart =
|
|
|
15
15
|
| AgentDataPart
|
|
16
16
|
| AgentImagePart
|
|
17
17
|
| AgentFilePart
|
|
18
|
+
| AgentReasoningPart
|
|
18
19
|
| AgentToolCallPart
|
|
19
20
|
| AgentToolResultPart;
|
|
20
21
|
export type AgentContentInput = string | AgentContentPart | readonly AgentContentPart[];
|
|
@@ -44,6 +45,19 @@ export interface AgentFilePart {
|
|
|
44
45
|
readonly name?: string;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* What a model thought before it answered or called a Tool. It stands in the
|
|
50
|
+
* assistant message it belongs to and goes back to the same provider with the
|
|
51
|
+
* dialog, unchanged: a provider may refuse reasoning it did not write.
|
|
52
|
+
*/
|
|
53
|
+
export interface AgentReasoningPart {
|
|
54
|
+
readonly kind: "reasoning";
|
|
55
|
+
/** What the provider lets an application see of the reasoning; may be empty. */
|
|
56
|
+
readonly text: string;
|
|
57
|
+
/** The provider's own record of the block (a signature, encrypted reasoning); opaque to everything else. */
|
|
58
|
+
readonly data?: JsonObject;
|
|
59
|
+
}
|
|
60
|
+
|
|
47
61
|
export interface AgentToolResultPart {
|
|
48
62
|
readonly kind: "tool-result";
|
|
49
63
|
readonly result: AgentToolResult;
|
|
@@ -77,6 +91,12 @@ export interface AgentToolCall {
|
|
|
77
91
|
readonly id: string;
|
|
78
92
|
readonly name: string;
|
|
79
93
|
readonly input: JsonValue;
|
|
94
|
+
/**
|
|
95
|
+
* Why the arguments the model wrote could not be read (broken or cut-off
|
|
96
|
+
* JSON). Such a call is never executed: the model gets this text as the
|
|
97
|
+
* Tool error `TOOL_ARGUMENTS_INVALID` and can write the call again.
|
|
98
|
+
*/
|
|
99
|
+
readonly inputError?: string;
|
|
80
100
|
readonly metadata: JsonObject;
|
|
81
101
|
}
|
|
82
102
|
|
|
@@ -84,6 +104,8 @@ export interface AgentToolCallOptions {
|
|
|
84
104
|
readonly id: string;
|
|
85
105
|
readonly name: string;
|
|
86
106
|
readonly input?: unknown;
|
|
107
|
+
/** Set by a model provider instead of throwing when the arguments are not valid JSON. */
|
|
108
|
+
readonly inputError?: string;
|
|
87
109
|
readonly metadata?: unknown;
|
|
88
110
|
}
|
|
89
111
|
|
|
@@ -247,9 +269,16 @@ export interface AgentModelRequestOptions {
|
|
|
247
269
|
export type AgentFinishReason = "stop" | "tool-calls" | "length" | "content-filter" | "error";
|
|
248
270
|
|
|
249
271
|
export interface AgentUsage {
|
|
272
|
+
/** The whole request as the provider counted it, tokens read from its prompt cache included. */
|
|
250
273
|
readonly inputTokens?: number;
|
|
251
274
|
readonly outputTokens?: number;
|
|
252
275
|
readonly totalTokens?: number;
|
|
276
|
+
/** The part of `inputTokens` the provider read from its prompt cache, at a lower price. */
|
|
277
|
+
readonly cacheReadTokens?: number;
|
|
278
|
+
/** The part of `inputTokens` the provider wrote to its prompt cache. */
|
|
279
|
+
readonly cacheWriteTokens?: number;
|
|
280
|
+
/** The part of `outputTokens` the model spent on reasoning. */
|
|
281
|
+
readonly reasoningTokens?: number;
|
|
253
282
|
readonly latencyMs?: number;
|
|
254
283
|
readonly cost?: AgentUsageCost;
|
|
255
284
|
readonly cacheHit?: boolean;
|
|
@@ -264,6 +293,9 @@ export interface AgentUsageInput {
|
|
|
264
293
|
readonly inputTokens?: number;
|
|
265
294
|
readonly outputTokens?: number;
|
|
266
295
|
readonly totalTokens?: number;
|
|
296
|
+
readonly cacheReadTokens?: number;
|
|
297
|
+
readonly cacheWriteTokens?: number;
|
|
298
|
+
readonly reasoningTokens?: number;
|
|
267
299
|
readonly latencyMs?: number;
|
|
268
300
|
readonly cost?: {
|
|
269
301
|
readonly amount: number;
|
|
@@ -409,6 +441,15 @@ export function agentFile(uri: string, options: { readonly mediaType?: string; r
|
|
|
409
441
|
});
|
|
410
442
|
}
|
|
411
443
|
|
|
444
|
+
export function agentReasoning(text: string, options: { readonly data?: unknown } = {}): AgentReasoningPart {
|
|
445
|
+
if (typeof text !== "string") throw new AgentSemanticError("reasoning.text must be a string.");
|
|
446
|
+
return freezeWithoutUndefined({
|
|
447
|
+
kind: "reasoning" as const,
|
|
448
|
+
text,
|
|
449
|
+
data: options.data === undefined ? undefined : normalizeJsonObject(options.data, "reasoning.data"),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
412
453
|
export function agentToolResultPart(result: AgentToolResult): AgentToolResultPart {
|
|
413
454
|
return Object.freeze({ kind: "tool-result", result: normalizeToolResult(result) });
|
|
414
455
|
}
|
|
@@ -431,6 +472,9 @@ export function agentMessage(role: AgentMessageRole, content: AgentContentInput,
|
|
|
431
472
|
if (role !== "assistant" && parts.some((part) => part.kind === "tool-call")) {
|
|
432
473
|
throw new AgentSemanticError("tool-call content parts require an assistant message.");
|
|
433
474
|
}
|
|
475
|
+
if (role !== "assistant" && parts.some((part) => part.kind === "reasoning")) {
|
|
476
|
+
throw new AgentSemanticError("reasoning content parts require an assistant message.");
|
|
477
|
+
}
|
|
434
478
|
const resultParts = parts.filter((part) => part.kind === "tool-result");
|
|
435
479
|
if (role === "tool") {
|
|
436
480
|
if (parts.length !== 1 || resultParts.length !== 1) {
|
|
@@ -454,11 +498,21 @@ export function agentMessage(role: AgentMessageRole, content: AgentContentInput,
|
|
|
454
498
|
});
|
|
455
499
|
}
|
|
456
500
|
|
|
501
|
+
/**
|
|
502
|
+
* The words of a message: its text parts joined, without reasoning, data and
|
|
503
|
+
* Tool calls. An empty string for a message that has no text or is absent.
|
|
504
|
+
*/
|
|
505
|
+
export function agentMessageText(message: AgentMessage | undefined): string {
|
|
506
|
+
if (message === undefined) return "";
|
|
507
|
+
return message.content.map((part) => part.kind === "text" ? part.text : "").join("");
|
|
508
|
+
}
|
|
509
|
+
|
|
457
510
|
export function agentToolCall(options: AgentToolCallOptions): AgentToolCall {
|
|
458
|
-
return
|
|
511
|
+
return freezeWithoutUndefined({
|
|
459
512
|
id: requiredName(options.id, "toolCall.id"),
|
|
460
513
|
name: requiredName(options.name, "toolCall.name"),
|
|
461
514
|
input: normalizeJsonValue(options.input ?? {}, "toolCall.input"),
|
|
515
|
+
inputError: optionalContentText(options.inputError, "toolCall.inputError"),
|
|
462
516
|
metadata: normalizeJsonObject(options.metadata ?? {}, "toolCall.metadata"),
|
|
463
517
|
});
|
|
464
518
|
}
|
|
@@ -661,6 +715,8 @@ function normalizeContentPart(part: AgentContentPart, field: string): AgentConte
|
|
|
661
715
|
return agentImage(part.uri, { mediaType: part.mediaType, detail: part.detail });
|
|
662
716
|
case "file":
|
|
663
717
|
return agentFile(part.uri, { mediaType: part.mediaType, name: part.name });
|
|
718
|
+
case "reasoning":
|
|
719
|
+
return agentReasoning(part.text, { data: part.data });
|
|
664
720
|
case "tool-result":
|
|
665
721
|
return agentToolResultPart(part.result);
|
|
666
722
|
case "tool-call":
|
|
@@ -715,6 +771,9 @@ function normalizeUsage(usage: AgentUsageInput): AgentUsage {
|
|
|
715
771
|
inputTokens: optionalNonNegativeInteger(usage.inputTokens, "usage.inputTokens"),
|
|
716
772
|
outputTokens: optionalNonNegativeInteger(usage.outputTokens, "usage.outputTokens"),
|
|
717
773
|
totalTokens: optionalNonNegativeInteger(usage.totalTokens, "usage.totalTokens"),
|
|
774
|
+
cacheReadTokens: optionalNonNegativeInteger(usage.cacheReadTokens, "usage.cacheReadTokens"),
|
|
775
|
+
cacheWriteTokens: optionalNonNegativeInteger(usage.cacheWriteTokens, "usage.cacheWriteTokens"),
|
|
776
|
+
reasoningTokens: optionalNonNegativeInteger(usage.reasoningTokens, "usage.reasoningTokens"),
|
|
718
777
|
latencyMs: optionalNonNegativeInteger(usage.latencyMs, "usage.latencyMs"),
|
|
719
778
|
cost: usage.cost
|
|
720
779
|
? Object.freeze({
|
package/core/cache/SPEC.md
CHANGED
|
@@ -161,7 +161,7 @@ flowchart TB
|
|
|
161
161
|
|
|
162
162
|
```ts
|
|
163
163
|
import { Cacheable, OutputCache, memory } from "@/core/cache";
|
|
164
|
-
import {
|
|
164
|
+
import { App } from "@/core/app";
|
|
165
165
|
|
|
166
166
|
@App({
|
|
167
167
|
modules: [AppModule],
|
|
@@ -196,7 +196,7 @@ class UserService implements IUserStore {
|
|
|
196
196
|
|
|
197
197
|
// UsersModule:
|
|
198
198
|
providers: [
|
|
199
|
-
scoped(IUserStore, UserService,
|
|
199
|
+
scoped(IUserStore, UserService), // codegen reads the constructor
|
|
200
200
|
],
|
|
201
201
|
```
|
|
202
202
|
|
|
@@ -207,7 +207,7 @@ The backend is connected from `@/core/infra/*`; the framework stays backend-agno
|
|
|
207
207
|
```ts
|
|
208
208
|
import { memory } from "@/core/cache";
|
|
209
209
|
import { Infra, redisConnect } from "@/core/infra";
|
|
210
|
-
import {
|
|
210
|
+
import { App } from "@/core/app";
|
|
211
211
|
import { redisConfig } from "@/app/config/redis.config";
|
|
212
212
|
import { AppModule } from "@/app/modules/App.module";
|
|
213
213
|
|
package/core/di/README.md
CHANGED
|
@@ -5,7 +5,10 @@ contracts are described in [MODULE.md](MODULE.md).
|
|
|
5
5
|
|
|
6
6
|
## Public API
|
|
7
7
|
|
|
8
|
-
- `index.ts`: the
|
|
8
|
+
- `index.ts`: the public DI entry point for applications (`bazis/core/di`): only the agreed
|
|
9
|
+
provider forms, tokens, `@Module` and the container.
|
|
10
|
+
- `lowLevel.ts`: `bazis/core/di/lowLevel` (0.138.0): the low-level provider forms (`DI`,
|
|
11
|
+
factories, dependency lists), for the framework's own modules and packages that extend it.
|
|
9
12
|
- `MODULE.md`: the contracts, boundaries and checks of the DI core.
|
|
10
13
|
|
|
11
14
|
## Container core
|
|
@@ -33,7 +36,7 @@ contracts are described in [MODULE.md](MODULE.md).
|
|
|
33
36
|
|
|
34
37
|
## Module layer
|
|
35
38
|
|
|
36
|
-
- `module/DI.ts`: low-level provider constructors.
|
|
39
|
+
- `module/DI.ts`: low-level provider constructors, exported only through `lowLevel.ts`.
|
|
37
40
|
- `module/shortcuts.ts`: the short API (`singleton/scoped/transient`).
|
|
38
41
|
- `module/createContainer.ts`: builds a container from modules.
|
|
39
42
|
- `module/encapsulation.ts`: build-time check of module isolation (`exports`).
|
|
@@ -81,7 +84,11 @@ contracts are described in [MODULE.md](MODULE.md).
|
|
|
81
84
|
(or the class alone), `singletonValue(TOKEN, value)`, `singletonAsyncFactory(TOKEN, [deps], async factory)`
|
|
82
85
|
and `...withEventSubscribers(Class)`. Codegen refuses anything else (`BAZIS_DI_PROVIDER_FORM`).
|
|
83
86
|
- The low-level path (`DI.classProvider(...)`, `DI.factoryProvider(...)`) stays for the kernel's own
|
|
84
|
-
plumbing and
|
|
87
|
+
plumbing and packages that extend the framework, never in a module's `providers`. Since 0.138.0
|
|
88
|
+
it is imported from `bazis/core/di/lowLevel`; `bazis/core/di` no longer exports `DI`, the
|
|
89
|
+
dependency-list argument of `scoped`/`singleton`/`transient`, `singletonFactory`,
|
|
90
|
+
`singletonFactoryWithResolver` or `singletonAsyncFactoryWithResolver`. Test `overrides` use
|
|
91
|
+
the agreed forms: `singletonValue(IStore, fake)`, `scoped(IStore, FakeStore)`.
|
|
85
92
|
|
|
86
93
|
## Contributing Checklist
|
|
87
94
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Claude adapter lives in its own entry: it imports `@anthropic-ai/sdk`,
|
|
3
|
+
* which only an application that uses Claude installs.
|
|
4
|
+
*/
|
|
5
|
+
export {
|
|
6
|
+
AnthropicModelProvider,
|
|
7
|
+
AnthropicProviderError,
|
|
8
|
+
anthropicAdapter,
|
|
9
|
+
type AnthropicAdapterOptions,
|
|
10
|
+
type AnthropicClient,
|
|
11
|
+
} from "./connectors/anthropic";
|