@jangts/agent-core 0.2.5 → 0.3.6

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 (111) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +386 -50
  3. package/development/docs/api/README.en.md +4 -0
  4. package/development/docs/api/README.md +4 -0
  5. package/development/docs/api/decorators.en.md +83 -0
  6. package/development/docs/api/decorators.md +108 -0
  7. package/development/docs/api/observability.en.md +37 -11
  8. package/development/docs/api/observability.md +64 -11
  9. package/development/docs/api/reference.en.md +469 -348
  10. package/development/docs/api/reference.md +225 -137
  11. package/development/docs/api/types.en.md +1 -0
  12. package/development/docs/api/types.md +1 -0
  13. package/development/docs/api/workflow-views.en.md +64 -0
  14. package/development/docs/api/workflow-views.md +78 -0
  15. package/development/docs/api/workflow.en.md +6 -2
  16. package/development/docs/api/workflow.md +6 -2
  17. package/development/skills/agent-core-app/SKILL.md +16 -0
  18. package/dist/cjs/capabilities/invocation.js +21 -1
  19. package/dist/cjs/capabilities/tool-runtime.js +8 -1
  20. package/dist/cjs/capabilities/tools.js +12 -17
  21. package/dist/cjs/diagnostics/index.js +51 -3
  22. package/dist/cjs/diagnostics/native-sub-flow.js +106 -0
  23. package/dist/cjs/diagnostics/sub-flow.js +278 -32
  24. package/dist/cjs/execution/engine.js +3 -2
  25. package/dist/cjs/execution/events.js +5 -5
  26. package/dist/cjs/execution/reporting.js +18 -28
  27. package/dist/cjs/execution/workflow-chain.js +211 -0
  28. package/dist/cjs/execution/workflow-graph.js +72 -0
  29. package/dist/cjs/execution/workflow.js +34 -6
  30. package/dist/cjs/kits/aggregation.js +17 -14
  31. package/dist/cjs/kits/answering/checks.js +2 -1
  32. package/dist/cjs/kits/answering/flow.js +24 -21
  33. package/dist/cjs/kits/answering/tasks.js +25 -20
  34. package/dist/cjs/kits/delivery.js +36 -24
  35. package/dist/cjs/kits/knowledge/catalog.js +33 -30
  36. package/dist/cjs/kits/policies.js +3 -2
  37. package/dist/cjs/kits/presentation/outputs.js +2 -1
  38. package/dist/cjs/kits/retrieval.js +7 -1
  39. package/dist/cjs/kits/tools.js +1 -0
  40. package/dist/cjs/model/decision-retry.js +2 -1
  41. package/dist/cjs/model/runner.js +20 -5
  42. package/dist/cjs/structs/execution/workflow-views.js +2 -0
  43. package/dist/cjs/support/context/nodes.js +48 -45
  44. package/dist/cjs/support/context/processing.js +6 -3
  45. package/dist/cjs/support/decorators/definition.js +42 -0
  46. package/dist/cjs/support/decorators/index.js +63 -0
  47. package/dist/cjs/support/decorators/pool.js +87 -0
  48. package/dist/cjs/support/decorators/types.js +2 -0
  49. package/dist/cjs/utils/invocation.js +1 -0
  50. package/dist/esm/capabilities/invocation.js +21 -1
  51. package/dist/esm/capabilities/tool-runtime.js +8 -1
  52. package/dist/esm/capabilities/tools.js +12 -17
  53. package/dist/esm/diagnostics/index.d.ts +1 -0
  54. package/dist/esm/diagnostics/index.js +48 -2
  55. package/dist/esm/diagnostics/native-sub-flow.d.ts +94 -0
  56. package/dist/esm/diagnostics/native-sub-flow.js +101 -0
  57. package/dist/esm/diagnostics/sub-flow.d.ts +56 -4
  58. package/dist/esm/diagnostics/sub-flow.js +271 -32
  59. package/dist/esm/execution/engine.d.ts +1 -0
  60. package/dist/esm/execution/engine.js +3 -2
  61. package/dist/esm/execution/events.js +5 -5
  62. package/dist/esm/execution/reporting.d.ts +6 -12
  63. package/dist/esm/execution/reporting.js +19 -29
  64. package/dist/esm/execution/workflow-chain.d.ts +3 -0
  65. package/dist/esm/execution/workflow-chain.js +208 -0
  66. package/dist/esm/execution/workflow-graph.d.ts +7 -0
  67. package/dist/esm/execution/workflow-graph.js +68 -0
  68. package/dist/esm/execution/workflow.d.ts +6 -0
  69. package/dist/esm/execution/workflow.js +34 -6
  70. package/dist/esm/kits/aggregation.d.ts +1 -1
  71. package/dist/esm/kits/aggregation.js +17 -14
  72. package/dist/esm/kits/answering/checks.js +2 -1
  73. package/dist/esm/kits/answering/flow.js +24 -21
  74. package/dist/esm/kits/answering/tasks.js +25 -20
  75. package/dist/esm/kits/delivery.d.ts +0 -9
  76. package/dist/esm/kits/delivery.js +36 -24
  77. package/dist/esm/kits/knowledge/catalog.js +33 -30
  78. package/dist/esm/kits/policies.d.ts +1 -1
  79. package/dist/esm/kits/policies.js +3 -2
  80. package/dist/esm/kits/presentation/outputs.js +2 -1
  81. package/dist/esm/kits/retrieval.js +7 -1
  82. package/dist/esm/kits/tools.js +1 -0
  83. package/dist/esm/model/decision-retry.d.ts +2 -0
  84. package/dist/esm/model/decision-retry.js +2 -1
  85. package/dist/esm/model/runner.d.ts +2 -0
  86. package/dist/esm/model/runner.js +20 -5
  87. package/dist/esm/structs/capabilities/invocation.d.ts +2 -0
  88. package/dist/esm/structs/capabilities/tools.d.ts +7 -3
  89. package/dist/esm/structs/common.d.ts +2 -0
  90. package/dist/esm/structs/debug.d.ts +36 -20
  91. package/dist/esm/structs/execution/workflow-views.d.ts +123 -0
  92. package/dist/esm/structs/execution/workflow-views.js +1 -0
  93. package/dist/esm/structs/execution/workflow.d.ts +4 -3
  94. package/dist/esm/structs/index.d.ts +1 -0
  95. package/dist/esm/structs/model/runtime.d.ts +2 -0
  96. package/dist/esm/support/context/nodes.js +48 -45
  97. package/dist/esm/support/context/processing.js +6 -3
  98. package/dist/esm/support/decorators/definition.d.ts +19 -0
  99. package/dist/esm/support/decorators/definition.js +38 -0
  100. package/dist/esm/support/decorators/index.d.ts +39 -0
  101. package/dist/esm/support/decorators/index.js +48 -0
  102. package/dist/esm/support/decorators/pool.d.ts +97 -0
  103. package/dist/esm/support/decorators/pool.js +83 -0
  104. package/dist/esm/support/decorators/types.d.ts +22 -0
  105. package/dist/esm/support/decorators/types.js +1 -0
  106. package/dist/esm/utils/invocation.d.ts +1 -0
  107. package/dist/esm/utils/invocation.js +1 -0
  108. package/package.json +10 -2
  109. package/dist/cjs/utils/debug.js +0 -30
  110. package/dist/esm/utils/debug.d.ts +0 -27
  111. package/dist/esm/utils/debug.js +0 -27
@@ -1,3 +1,4 @@
1
+ import type { WorkflowChainSource } from './workflow-views.js';
1
2
  /**
2
3
  * Workflow contract: a state graph whose nodes report their own use of model and tools.
3
4
  *
@@ -66,8 +67,6 @@ export interface WorkflowNodeReport {
66
67
  readonly usedModel?: boolean;
67
68
  /** App-defined progress detail; collected only when content logging is enabled. */
68
69
  readonly payload?: unknown;
69
- /** Logger owned by this visit, used to report sub-steps. */
70
- readonly subs?: import('../debug.js').SubFlowLogger;
71
70
  }
72
71
  /** One node of the graph. */
73
72
  export type WorkflowNode<State, Result, Runtime = undefined> = {
@@ -94,7 +93,7 @@ export type WorkflowNode<State, Result, Runtime = undefined> = {
94
93
  readonly requires?: readonly (keyof State & string)[];
95
94
  /** State keys this node is expected to produce; validated after `run`. */
96
95
  readonly provides?: readonly (keyof State & string)[];
97
- /** Candidate successors, evaluated in order. */
96
+ /** Candidate successors, evaluated in order. A getter must be pure; registration reads it once. */
98
97
  readonly routes: readonly WorkflowRoute<State, Result, Runtime>[];
99
98
  /** Return only changed fields. Core commits them after cancellation and output checks. */
100
99
  readonly run?: (state: Readonly<State>, context: WorkflowContext<Runtime>) => void | Partial<State> | Promise<void | Partial<State>>;
@@ -103,6 +102,8 @@ export type WorkflowNode<State, Result, Runtime = undefined> = {
103
102
  export type AgentWorkflowDefinition<State, Result, Runtime = undefined> = {
104
103
  /** Stable workflow id, also reported in every event. */
105
104
  readonly id: string;
105
+ /** App-owned authorized diagnostic reader; no global run storage is installed. */
106
+ readonly chainSource?: WorkflowChainSource;
106
107
  /** Definition revision; lets hosts distinguish executions of different graph versions. */
107
108
  readonly revision?: string;
108
109
  readonly nodes: readonly WorkflowNode<State, Result, Runtime>[];
@@ -55,3 +55,4 @@ export type * from './invocation.js';
55
55
  export type * from './debug.js';
56
56
  export type * from './execution/events.js';
57
57
  export type * from './execution/cache.js';
58
+ export type * from './execution/workflow-views.js';
@@ -175,6 +175,8 @@ export type ModelRunnerOptions<Transports extends readonly ModelTransportDefinit
175
175
  } & ModelRunnerSettings;
176
176
  /** One model call request. The input shape selects prompt mode or message mode. */
177
177
  export type ModelCallRequest<Transport = string> = {
178
+ /** Record this call in the supplied logger; omission adds no sub-flow record. */
179
+ readonly logger?: import('../debug.js').SubFlowLogger;
178
180
  /** Additional cancellation can shorten, never replace, the Runner lifetime. */
179
181
  readonly signal?: AbortSignal;
180
182
  /** Parent observation scope for this invocation; never changes execution budget or signal. */
@@ -9,6 +9,7 @@
9
9
  * Main exports: `createContextProcessingNode`, `createContextSelectionNode`,
10
10
  * `createContextReductionNode` and their result/definition types.
11
11
  */
12
+ import { nativeSubFlowAsync } from '../../diagnostics/native-sub-flow.js';
12
13
  import { beginContextProcessing, finishContextProcessing, chooseSteps, applyReduction, needsReduction, watermarks, validateReductionTargets, contextPolicyCatalog, validateContextPolicy, } from './processing.js';
13
14
  import { modelRunnerDefaults } from '../../model/defaults.js';
14
15
  import { synchronous } from '../../utils/synchronous.js';
@@ -124,53 +125,55 @@ function contextNode(definition, execute) {
124
125
  onErrorRouteId: definition.onErrorRouteId,
125
126
  routes: definition.routes,
126
127
  async run(state, context) {
127
- // The node timeout is an additional bound; parent cancellation still wins.
128
- const signal = combineSignals(context.signal, AbortSignal.timeout(timeoutMs));
129
- const prepared = selectPrepared
130
- ? selectPrepared(state, context)
131
- : context.model.prepare(typeof request === 'function' ? request(state, context) : request);
132
- const input = synchronous(plan(state, context), 'Context plans must be synchronous');
133
- // An empty measurement reports the allowance left after transport, instruction and schema
134
- // overhead, so processing must fit inside whichever limit is tighter.
135
- const overhead = prepared.measure([]);
136
- const fullLimit = Math.min(input.maxInputChars, overhead.maxInputChars);
137
- const bodyLimit = fullLimit;
138
- breakWhile(bodyLimit < 1, 'No input capacity for context');
139
- const work = beginContextProcessing({
140
- fragments: input.fragments,
141
- additionalMessages: input.additionalMessages,
142
- originalFragments: input.originalFragments,
143
- mutableFragmentIds: input.mutableFragmentIds,
144
- maxInputChars: bodyLimit,
145
- }, policy, {
146
- stageId: prepared.stageId,
147
- signal,
148
- diagnostics: context.diagnostics,
149
- compressors,
150
- selectors,
151
- // Read lazily so handlers see the live budget rather than the value at preparation.
152
- remainingCalls: () => prepared.remainingCalls,
153
- callModel: (options) => context.model.invoke({
154
- ...options,
155
- modelId: options.modelId ?? prepared.modelId,
128
+ return nativeSubFlowAsync(context, 'context', definition.id, async () => {
129
+ // The node timeout is an additional bound; parent cancellation still wins.
130
+ const signal = combineSignals(context.signal, AbortSignal.timeout(timeoutMs));
131
+ const prepared = selectPrepared
132
+ ? selectPrepared(state, context)
133
+ : context.model.prepare(typeof request === 'function' ? request(state, context) : request);
134
+ const input = synchronous(plan(state, context), 'Context plans must be synchronous');
135
+ // An empty measurement reports the allowance left after transport, instruction and schema
136
+ // overhead, so processing must fit inside whichever limit is tighter.
137
+ const overhead = prepared.measure([]);
138
+ const fullLimit = Math.min(input.maxInputChars, overhead.maxInputChars);
139
+ const bodyLimit = fullLimit;
140
+ breakWhile(bodyLimit < 1, 'No input capacity for context');
141
+ const work = beginContextProcessing({
142
+ fragments: input.fragments,
143
+ additionalMessages: input.additionalMessages,
144
+ originalFragments: input.originalFragments,
145
+ mutableFragmentIds: input.mutableFragmentIds,
146
+ maxInputChars: bodyLimit,
147
+ }, policy, {
148
+ stageId: prepared.stageId,
156
149
  signal,
157
- }),
158
- ...(overhead.counterId
159
- ? {
160
- tokenBudget: {
161
- counterId: overhead.counterId,
162
- inputLimit: overhead.inputTokenLimit,
163
- measure: (messages) => prepared.measure(messages).inputTokens,
164
- },
165
- }
166
- : {}),
150
+ diagnostics: context.diagnostics,
151
+ compressors,
152
+ selectors,
153
+ // Read lazily so handlers see the live budget rather than the value at preparation.
154
+ remainingCalls: () => prepared.remainingCalls,
155
+ callModel: (options) => context.model.invoke({
156
+ ...options,
157
+ modelId: options.modelId ?? prepared.modelId,
158
+ signal,
159
+ }),
160
+ ...(overhead.counterId
161
+ ? {
162
+ tokenBudget: {
163
+ counterId: overhead.counterId,
164
+ inputLimit: overhead.inputTokenLimit,
165
+ measure: (messages) => prepared.measure(messages).inputTokens,
166
+ },
167
+ }
168
+ : {}),
169
+ });
170
+ context.report({ payload: { phase: 'processing', fragmentCount: input.fragments.length } });
171
+ const result = await execute(work, prepared, fullLimit, state, context);
172
+ // Never report or commit a result produced after cancellation.
173
+ signal.throwIfAborted();
174
+ context.report({ payload: { phase: 'processed', report: work.report } });
175
+ return synchronous(update(result, state), 'Context node updates must be synchronous');
167
176
  });
168
- context.report({ payload: { phase: 'processing', fragmentCount: input.fragments.length } });
169
- const result = await execute(work, prepared, fullLimit, state, context);
170
- // Never report or commit a result produced after cancellation.
171
- signal.throwIfAborted();
172
- context.report({ payload: { phase: 'processed', report: work.report } });
173
- return synchronous(update(result, state), 'Context node updates must be synchronous');
174
177
  },
175
178
  };
176
179
  }
@@ -11,6 +11,7 @@
11
11
  * `finishContextProcessing` (staged runs), the policy/reduction catalogs and the individual
12
12
  * validation, measurement and reduction helpers used by workflow nodes.
13
13
  */
14
+ import { nativeSubFlowAsync } from '../../diagnostics/native-sub-flow.js';
14
15
  import { snapshotFragment, snapshotMessage } from './snapshot.js';
15
16
  import { auditContextChange as audit } from '../../context/audit.js';
16
17
  import { historyGroups, validateRecentHistory } from './history.js';
@@ -595,7 +596,9 @@ export function finishContextProcessing(state) {
595
596
  * @throws {BudgetError} When a handler exhausts its call allowance.
596
597
  */
597
598
  export async function processContext(plan, policy, ports) {
598
- const state = beginContextProcessing(plan, policy, ports);
599
- await contextPolicyCatalog[policy.mode].run(state);
600
- return finishContextProcessing(state);
599
+ return nativeSubFlowAsync(ports, 'context', policy.mode, async () => {
600
+ const state = beginContextProcessing(plan, policy, ports);
601
+ await contextPolicyCatalog[policy.mode].run(state);
602
+ return finishContextProcessing(state);
603
+ });
601
604
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Internal decoration: construct definition classes or bind methods to their real host,
3
+ * compile through existing entry points and automatically register in the kind's root.
4
+ */
5
+ import { type DefinitionKind, type IdentifiedDefinition } from './pool.js';
6
+ type Handler = (this: any, ...args: any[]) => any;
7
+ /** Preserve union branches when removing the decorator-owned id. */
8
+ export type DefinitionBody<Definition> = Definition extends unknown ? Omit<Definition, 'id'> : never;
9
+ /** Namespaces group external registration without changing Core options. */
10
+ export interface DecorationOptions {
11
+ readonly ns?: string | readonly string[];
12
+ }
13
+ export type ClassDecoration<Definition extends IdentifiedDefinition> = <Constructor extends new () => DefinitionBody<Definition>>(target: Constructor, context: ClassDecoratorContext<Constructor>) => void;
14
+ export type MethodDecoration<Method extends Handler> = <This>(method: (this: This, ...args: Parameters<Method>) => ReturnType<Method>, context: ClassMethodDecoratorContext<This, (this: This, ...args: Parameters<Method>) => ReturnType<Method>>) => void;
15
+ /** Class definitions register once after static initialization and successful construction. */
16
+ export declare function classDecoration<Definition extends IdentifiedDefinition>(kind: DefinitionKind, id: string, options: DecorationOptions, compile: (value: Definition) => Definition): ClassDecoration<Definition>;
17
+ /** Method initializers bind and register automatically; no scanning or caller-side bind is needed. */
18
+ export declare function methodDecoration<Definition extends IdentifiedDefinition, Method extends Handler>(kind: DefinitionKind, id: string, metadata: DefinitionBody<Definition>, handlerKey: keyof Definition, options: DecorationOptions, compile: (value: Definition) => Definition): MethodDecoration<Method>;
19
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Internal decoration: construct definition classes or bind methods to their real host,
3
+ * compile through existing entry points and automatically register in the kind's root.
4
+ */
5
+ import { registerDefinition } from './pool.js';
6
+ /** Restore only the decorator-owned id, preserving instance identity, getters and private state. */
7
+ function withId(id, instance) {
8
+ if ('id' in instance)
9
+ throw new TypeError('Decoration supplies id; do not also define it on the class');
10
+ Object.defineProperty(instance, 'id', { value: id, enumerable: true });
11
+ return instance;
12
+ }
13
+ /** Class definitions register once after static initialization and successful construction. */
14
+ export function classDecoration(kind, id, options, compile) {
15
+ return (_target, context) => {
16
+ if (context?.kind !== 'class')
17
+ throw new TypeError('Use standard class decorator semantics');
18
+ context.addInitializer(function () {
19
+ registerDefinition(kind, compile(withId(id, new this())), options.ns);
20
+ });
21
+ };
22
+ }
23
+ /** Method initializers bind and register automatically; no scanning or caller-side bind is needed. */
24
+ export function methodDecoration(kind, id, metadata, handlerKey, options, compile) {
25
+ return (method, context) => {
26
+ if (context?.kind !== 'method' || context.static || context.private)
27
+ throw new TypeError('Use a standard public instance method decorator');
28
+ context.addInitializer(function () {
29
+ const value = { ...metadata, [handlerKey]: method.bind(this) };
30
+ for (const key of Reflect.ownKeys(value)) {
31
+ const member = value[key];
32
+ if (typeof member === 'function')
33
+ value[key] = member.bind(this);
34
+ }
35
+ registerDefinition(kind, compile(withId(id, value)), options.ns);
36
+ });
37
+ };
38
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Optional standard decorators. Classes are definition implementations; public instance methods
3
+ * keep their original host. Only definitions are created here, never Engines or ModelRunners.
4
+ */
5
+ import type { WorkflowNode, AgentWorkflowDefinition } from '../../structs/execution/workflow.js';
6
+ import type { AgentCoreEngineSetup } from '../../execution/engine.js';
7
+ import type { ToolDefinition } from '../../structs/capabilities/tools.js';
8
+ import type { ContextFragmentConfig } from '../../structs/support/context.js';
9
+ import type { ModelRunnerOptions } from '../../structs/model/runtime.js';
10
+ import { type ClassDecoration, type MethodDecoration, type DecorationOptions } from './definition.js';
11
+ import type { IdentifiedDefinition } from './pool.js';
12
+ export { nodePool, toolPool, fragmentPool, workflowPool, agentPool, modelPool } from './pool.js';
13
+ export type { IdentifiedDefinition, DefinitionReader } from './pool.js';
14
+ export type { ClassDecoration, MethodDecoration, DecorationOptions } from './definition.js';
15
+ export type { Node, Tool, Fragment, Workflow, Agent, Model } from './types.js';
16
+ /** Method metadata describes the adapted handler, with optional external namespaces. */
17
+ type MethodOptions<Definition extends IdentifiedDefinition, Key extends keyof Definition> = Omit<Definition, 'id' | Key> & DecorationOptions;
18
+ /** Instantiate a node definition class once, injecting its id; graph validation stays in Workflow. */
19
+ export declare function node<State = any, Result = unknown, App = any>(id: string, options?: DecorationOptions): ClassDecoration<WorkflowNode<State, Result, App>>;
20
+ /** Bind a named host method as run without renaming or manually binding the original object. */
21
+ export declare function node<State = any, Result = unknown, App = any>(id: string, options: MethodOptions<WorkflowNode<State, Result, App>, 'run'>): MethodDecoration<NonNullable<WorkflowNode<State, Result, App>['run']>>;
22
+ /** Instantiate a tool definition class; schema and authorization stay with existing defineTool. */
23
+ export declare function tool<Input = unknown, Output = unknown, App = any>(id: string, options?: DecorationOptions): ClassDecoration<ToolDefinition<Input, Output, App>>;
24
+ /** Bind an existing host method as the tool's execute callback. */
25
+ export declare function tool<Input = unknown, Output = unknown, App = any>(id: string, options: MethodOptions<ToolDefinition<Input, Output, App>, 'execute'>): MethodDecoration<ToolDefinition<Input, Output, App>['execute']>;
26
+ /** Instantiate a self-describing fragment class; the original builder owns formatting checks. */
27
+ export declare function fragment<Input = unknown>(id: string, options?: DecorationOptions): ClassDecoration<ContextFragmentConfig<Input>>;
28
+ /** Bind a host's formatter method into an original fragment configuration. */
29
+ export declare function fragment<Input = unknown>(id: string, options: MethodOptions<ContextFragmentConfig<Input>, 'format'>): MethodDecoration<ContextFragmentConfig<Input>['format']>;
30
+ /** Instantiate and validate workflow options, leaving actual execution with the original Engine. */
31
+ export declare function workflow<State = any, Result = unknown, App = any>(id: string, options?: DecorationOptions): ClassDecoration<AgentWorkflowDefinition<State, Result, App>>;
32
+ /** Agent id is an external pool identity, not an override of Core branch or request identity. */
33
+ export type AgentOptions<State = unknown, Result = unknown, App = undefined> = AgentCoreEngineSetup<State, Result, App> & IdentifiedDefinition;
34
+ /** Instantiate an Engine configuration class without creating an Engine, budget or request. */
35
+ export declare function agent<State = unknown, Result = unknown, App = undefined>(id: string, options?: DecorationOptions): ClassDecoration<AgentOptions<State, Result, App>>;
36
+ /** Model id is only a pool key; Core still owns the configured model/transport identities. */
37
+ export type ModelOptions = ModelRunnerOptions & IdentifiedDefinition;
38
+ /** Instantiate and validate model options without creating a Runner or consuming calls. */
39
+ export declare function model(id: string, options?: DecorationOptions): ClassDecoration<ModelOptions>;
@@ -0,0 +1,48 @@
1
+ import { defineTool } from '../tools.js';
2
+ import { defineWorkflow } from '../../execution/workflow.js';
3
+ import { defineContextFragments } from '../context/assembly.js';
4
+ import { snapshotModelBinding } from '../../model/binding.js';
5
+ import { classDecoration, methodDecoration, } from './definition.js';
6
+ export { nodePool, toolPool, fragmentPool, workflowPool, agentPool, modelPool } from './pool.js';
7
+ /** Internal conversion shared by node/tool/fragment; never a public free-function adapter. */
8
+ function decorate(kind, id, options, key, compile) {
9
+ return (value, context) => {
10
+ if (context?.kind === 'class') {
11
+ classDecoration(kind, id, options, compile)(value, context);
12
+ }
13
+ else {
14
+ const { ns, ...metadata } = options;
15
+ methodDecoration(kind, id, metadata, key, { ns }, compile)(value, context);
16
+ }
17
+ };
18
+ }
19
+ export function node(id, options = {}) {
20
+ return decorate('node', id, options, 'run', (value) => value);
21
+ }
22
+ export function tool(id, options = {}) {
23
+ return decorate('tool', id, options, 'execute', (value) => defineTool(value));
24
+ }
25
+ export function fragment(id, options = {}) {
26
+ return decorate('fragment', id, options, 'format', (value) => {
27
+ defineContextFragments([value]);
28
+ return value;
29
+ });
30
+ }
31
+ /** Instantiate and validate workflow options, leaving actual execution with the original Engine. */
32
+ export function workflow(id, options = {}) {
33
+ return classDecoration('workflow', id, options, (value) => {
34
+ defineWorkflow(value);
35
+ return value;
36
+ });
37
+ }
38
+ /** Instantiate an Engine configuration class without creating an Engine, budget or request. */
39
+ export function agent(id, options = {}) {
40
+ return classDecoration('agent', id, options, (value) => value);
41
+ }
42
+ /** Instantiate and validate model options without creating a Runner or consuming calls. */
43
+ export function model(id, options = {}) {
44
+ return classDecoration('model', id, options, (value) => {
45
+ snapshotModelBinding(value);
46
+ return value;
47
+ });
48
+ }
@@ -0,0 +1,97 @@
1
+ import type { WorkflowNode, AgentWorkflowDefinition } from '../../structs/execution/workflow.js';
2
+ import type { ToolDefinition } from '../../structs/capabilities/tools.js';
3
+ import type { ContextFragmentConfig } from '../../structs/support/context.js';
4
+ /** Registration identity; ids are unique within one kind and namespace. */
5
+ export interface IdentifiedDefinition {
6
+ readonly id: string;
7
+ }
8
+ /** Read-only roots. A typed read describes the expected contract, not a static id/type lookup. */
9
+ export interface DefinitionReader<Definition extends IdentifiedDefinition> {
10
+ get<Value extends Definition = Definition>(id: string, ns?: string): Value | undefined;
11
+ getAll<Value extends Definition = Definition>(ns?: string): readonly Value[];
12
+ pick<Value extends Definition = Definition>(ids: readonly string[], ns?: string): readonly Value[];
13
+ }
14
+ /** Enumerable categories drive allocation and lookup; no additional runtime dispatch layer. */
15
+ export declare const definitionKinds: readonly ["node", "tool", "fragment", "workflow", "agent", "model"];
16
+ export type DefinitionKind = (typeof definitionKinds)[number];
17
+ /** Preflight every destination so a conflict never leaves partial multi-namespace registration. */
18
+ export declare function registerDefinition(kind: DefinitionKind, definition: IdentifiedDefinition, ns?: string | readonly string[]): void;
19
+ /** Decorated nodes; ids are unique within each namespace. */
20
+ export declare const nodePool: DefinitionReader<WorkflowNode<any, any, any>>;
21
+ /** Decorated tools; namespaces isolate otherwise identical ids. */
22
+ export declare const toolPool: DefinitionReader<ToolDefinition<any, any, any, string>>;
23
+ /** Decorated fragment configurations; use the original builder to assemble them. */
24
+ export declare const fragmentPool: DefinitionReader<ContextFragmentConfig<any>>;
25
+ /** Decorated workflow definitions; execution still belongs to the original Engine. */
26
+ export declare const workflowPool: DefinitionReader<AgentWorkflowDefinition<any, any, any>>;
27
+ /** Decorated Engine options; request-specific App contracts are supplied on typed reads. */
28
+ export declare const agentPool: DefinitionReader<(Omit<import("../../index.js").AgentCoreEngineOptions & {
29
+ readonly branch?: import("../../index.js").AgentBranch;
30
+ readonly tools?: {
31
+ invoke: (request: import("../../index.js").ToolInvocationRequest) => Promise<import("../../index.js").ToolResult<unknown>>;
32
+ } | Pick<import("../../index.js").ToolRegistry<import("../../index.js").ExecutableTool>, "invokeAsync">;
33
+ } & {
34
+ model?: import("../../index.js").AgentModelBinding;
35
+ models?: never;
36
+ } & {
37
+ workflow?: undefined;
38
+ app?: never;
39
+ }, "app"> & {
40
+ readonly app?: unknown;
41
+ } & IdentifiedDefinition) | (Omit<import("../../index.js").AgentCoreEngineOptions & {
42
+ readonly branch?: import("../../index.js").AgentBranch;
43
+ readonly tools?: {
44
+ invoke: (request: import("../../index.js").ToolInvocationRequest) => Promise<import("../../index.js").ToolResult<unknown>>;
45
+ } | Pick<import("../../index.js").ToolRegistry<import("../../index.js").ExecutableTool>, "invokeAsync">;
46
+ } & {
47
+ model?: string;
48
+ models: Readonly<Record<string, import("../../index.js").AgentModelBinding>>;
49
+ maxModelCalls?: number;
50
+ } & {
51
+ workflow?: undefined;
52
+ app?: never;
53
+ }, "app"> & {
54
+ readonly app?: unknown;
55
+ } & IdentifiedDefinition) | (Omit<import("../../index.js").AgentCoreEngineOptions & {
56
+ readonly branch?: import("../../index.js").AgentBranch;
57
+ readonly tools?: {
58
+ invoke: (request: import("../../index.js").ToolInvocationRequest) => Promise<import("../../index.js").ToolResult<unknown>>;
59
+ } | Pick<import("../../index.js").ToolRegistry<import("../../index.js").ExecutableTool>, "invokeAsync">;
60
+ } & {
61
+ model?: import("../../index.js").AgentModelBinding;
62
+ models?: never;
63
+ } & {
64
+ readonly workflow: import("../../index.js").AgentWorkflow<any, any, any>;
65
+ } & {
66
+ readonly app?: undefined;
67
+ }, "app"> & {
68
+ readonly app?: unknown;
69
+ } & IdentifiedDefinition) | (Omit<import("../../index.js").AgentCoreEngineOptions & {
70
+ readonly branch?: import("../../index.js").AgentBranch;
71
+ readonly tools?: {
72
+ invoke: (request: import("../../index.js").ToolInvocationRequest) => Promise<import("../../index.js").ToolResult<unknown>>;
73
+ } | Pick<import("../../index.js").ToolRegistry<import("../../index.js").ExecutableTool>, "invokeAsync">;
74
+ } & {
75
+ model?: string;
76
+ models: Readonly<Record<string, import("../../index.js").AgentModelBinding>>;
77
+ maxModelCalls?: number;
78
+ } & {
79
+ readonly workflow: import("../../index.js").AgentWorkflow<any, any, any>;
80
+ } & {
81
+ readonly app?: undefined;
82
+ }, "app"> & {
83
+ readonly app?: unknown;
84
+ } & IdentifiedDefinition)>;
85
+ /** Decorated Runner options; reading does not create a Runner or spend model calls. */
86
+ export declare const modelPool: DefinitionReader<{
87
+ provider: import("../../index.js").ChatProvider;
88
+ execution?: import("../../index.js").ExecutionOptions;
89
+ transports?: readonly import("../../index.js").ModelTransportDefinition<unknown>[] | undefined;
90
+ sink?: {
91
+ output?: import("../../index.js").ModelRunnerPorts["onOutput"];
92
+ };
93
+ errors?: {
94
+ inputBudget?: import("../../index.js").ModelRunnerPorts["inputBudgetError"];
95
+ outputBudget?: import("../../index.js").ModelRunnerPorts["outputBudgetError"];
96
+ };
97
+ } & import("../../index.js").ModelRunnerSettings & IdentifiedDefinition>;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * One process-realm definition registry per kind. The shared symbol makes ESM/CJS copies
3
+ * consume the same stores; consumers receive frozen read-only views, never pool factories.
4
+ */
5
+ import { ProtocolRegistry } from '../../utils/protocol-registry.js';
6
+ /** Enumerable categories drive allocation and lookup; no additional runtime dispatch layer. */
7
+ export const definitionKinds = ['node', 'tool', 'fragment', 'workflow', 'agent', 'model'];
8
+ const sharedKey = Symbol.for('@jangts/agent-core/decorator-roots/v2');
9
+ const realm = globalThis;
10
+ /** Allocate once per realm, including when another module format initialized the roots first. */
11
+ function sharedPools() {
12
+ const existing = realm[sharedKey];
13
+ if (existing) {
14
+ if (existing.version !== 2 || definitionKinds.some((kind) => !existing.stores[kind]))
15
+ throw new TypeError('Incompatible decorator root registry');
16
+ return existing;
17
+ }
18
+ const stores = Object.fromEntries(definitionKinds.map((kind) => [kind, new Map()]));
19
+ const pools = Object.freeze({ version: 2, stores: Object.freeze(stores) });
20
+ Object.defineProperty(globalThis, sharedKey, { value: pools });
21
+ return pools;
22
+ }
23
+ /** Namespace is an exact, nonblank key; no trimming, fallback or hierarchy is applied. */
24
+ function namespace(value) {
25
+ if (typeof value !== 'string' || !value.trim())
26
+ throw new TypeError('Decorator namespace must be a nonempty string');
27
+ return value;
28
+ }
29
+ /** Preflight every destination so a conflict never leaves partial multi-namespace registration. */
30
+ export function registerDefinition(kind, definition, ns = 'default') {
31
+ if (typeof definition.id !== 'string' || !definition.id.trim())
32
+ throw new TypeError('Invalid decorator definition id');
33
+ const requested = typeof ns === 'string' ? [ns] : ns;
34
+ if (!Array.isArray(requested) || requested.length === 0)
35
+ throw new TypeError('Decorator ns must be a namespace or a nonempty namespace array');
36
+ const names = [...new Set(requested.map(namespace))];
37
+ const store = sharedPools().stores[kind];
38
+ for (const name of names) {
39
+ if (store.get(name)?.has(definition.id))
40
+ throw new Error(`Duplicate ${kind} definition: ${definition.id} (ns: ${name})`);
41
+ }
42
+ for (const name of names) {
43
+ let entries = store.get(name);
44
+ if (!entries)
45
+ store.set(name, (entries = new ProtocolRegistry()));
46
+ entries.set(definition.id, definition);
47
+ }
48
+ }
49
+ /** Root reads never instantiate or execute definitions; array results are immutable snapshots. */
50
+ function reader(kind) {
51
+ const store = sharedPools().stores[kind];
52
+ return Object.freeze({
53
+ get(id, ns = 'default') {
54
+ return store.get(namespace(ns))?.get(id);
55
+ },
56
+ getAll(ns = 'default') {
57
+ return Object.freeze([...(store.get(namespace(ns))?.values() ?? [])]);
58
+ },
59
+ pick(ids, ns = 'default') {
60
+ const entries = store.get(namespace(ns));
61
+ const selected = new ProtocolRegistry();
62
+ for (const id of ids) {
63
+ const definition = entries?.get(id);
64
+ if (!definition)
65
+ throw new TypeError(`Unknown ${kind} definition: ${id} (ns: ${ns})`);
66
+ selected.set(id, definition);
67
+ }
68
+ return Object.freeze([...selected.values()]);
69
+ },
70
+ });
71
+ }
72
+ /** Decorated nodes; ids are unique within each namespace. */
73
+ export const nodePool = reader('node');
74
+ /** Decorated tools; namespaces isolate otherwise identical ids. */
75
+ export const toolPool = reader('tool');
76
+ /** Decorated fragment configurations; use the original builder to assemble them. */
77
+ export const fragmentPool = reader('fragment');
78
+ /** Decorated workflow definitions; execution still belongs to the original Engine. */
79
+ export const workflowPool = reader('workflow');
80
+ /** Decorated Engine options; request-specific App contracts are supplied on typed reads. */
81
+ export const agentPool = reader('agent');
82
+ /** Decorated Runner options; reading does not create a Runner or spend model calls. */
83
+ export const modelPool = reader('model');
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Class implementation contracts for definition decorators. The decorator supplies id;
3
+ * classes implement these named contracts instead of repeating Omit at every use site.
4
+ */
5
+ import type { WorkflowNode, AgentWorkflowDefinition } from '../../structs/execution/workflow.js';
6
+ import type { ToolDefinition } from '../../structs/capabilities/tools.js';
7
+ import type { ContextFragmentConfig } from '../../structs/support/context.js';
8
+ import type { AgentCoreEngineSetup } from '../../execution/engine.js';
9
+ import type { ModelRunnerOptions } from '../../structs/model/runtime.js';
10
+ import type { DefinitionBody } from './definition.js';
11
+ /** A node implementation; its public id is supplied by @node. */
12
+ export type Node<State, Result, App = undefined> = DefinitionBody<WorkflowNode<State, Result, App>>;
13
+ /** A tool implementation with the original schema, authorization and execution contracts. */
14
+ export type Tool<Input = unknown, Output = unknown, App = unknown> = DefinitionBody<ToolDefinition<Input, Output, App>>;
15
+ /** A formatter and its original provenance, trust and budget configuration. */
16
+ export type Fragment<Input = unknown> = DefinitionBody<ContextFragmentConfig<Input>>;
17
+ /** A complete graph definition whose workflow id is supplied by @workflow. */
18
+ export type Workflow<State, Result, App = undefined> = DefinitionBody<AgentWorkflowDefinition<State, Result, App>>;
19
+ /** Original Engine configuration; union branches retain their required App/model relationships. */
20
+ export type Agent<State = unknown, Result = unknown, App = undefined> = AgentCoreEngineSetup<State, Result, App>;
21
+ /** Original Runner configuration; the decorator's pool id is not a model identity override. */
22
+ export type Model = ModelRunnerOptions;
@@ -0,0 +1 @@
1
+ export {};
@@ -38,6 +38,7 @@ export declare function invocationResult<Value, Metadata extends InvocationMetad
38
38
  * @throws TypeError When the arity or the request shape is invalid.
39
39
  */
40
40
  export declare function toolInvocation(request: import('../structs/capabilities/tools.js').ToolInvocationRequest, arity: number): {
41
+ logger: import("../index.js").SubFlowLogger | undefined;
41
42
  toolId: string;
42
43
  input: unknown;
43
44
  diagnostics: import("../index.js").DiagnosticContext | undefined;
@@ -41,6 +41,7 @@ export function toolInvocation(request, arity) {
41
41
  throw new TypeError('Use one tool request with toolId and input');
42
42
  }
43
43
  return {
44
+ logger: request.logger,
44
45
  toolId: request.toolId,
45
46
  input: request.input,
46
47
  diagnostics: request.diagnostics,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jangts/agent-core",
3
- "version": "0.2.5",
3
+ "version": "0.3.6",
4
4
  "type": "commonjs",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Platform-independent agent runtime and configurable modules for evidence, delivery, policies and interactions",
@@ -241,6 +241,11 @@
241
241
  "types": "./dist/esm/mock/web.d.ts",
242
242
  "import": "./dist/esm/mock/web.js",
243
243
  "require": "./dist/cjs/mock/web.js"
244
+ },
245
+ "./support/decorators": {
246
+ "types": "./dist/esm/support/decorators/index.d.ts",
247
+ "import": "./dist/esm/support/decorators/index.js",
248
+ "require": "./dist/cjs/support/decorators/index.js"
244
249
  }
245
250
  },
246
251
  "scripts": {
@@ -254,7 +259,8 @@
254
259
  "minisearch": "^7.2.0"
255
260
  },
256
261
  "devDependencies": {
257
- "typescript": "~6.0.2"
262
+ "typescript": "~6.0.2",
263
+ "esbuild": "0.28.2"
258
264
  },
259
265
  "keywords": [
260
266
  "ai",
@@ -271,6 +277,8 @@
271
277
  "openai-compatible",
272
278
  "context-management",
273
279
  "streaming",
280
+ "decorators",
281
+ "typescript-decorators",
274
282
  "typescript",
275
283
  "nodejs",
276
284
  "browser"
@@ -1,30 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.captureSubSteps = captureSubSteps;
4
- const sub_flow_js_1 = require("../diagnostics/sub-flow.js");
5
- const synchronous_js_1 = require("./synchronous.js");
6
- /**
7
- * Read the current sub-step log of one execution visit.
8
- *
9
- * A missing reader or a logger that yields nothing produces the empty shape, which
10
- * lets callers spread the result unconditionally. Failures are reported as one
11
- * dropped step instead of propagating, because diagnostics must not break execution.
12
- *
13
- * @param read - Getter for the current logger; omitted when the caller has no logger.
14
- * @param identity - Diagnostic identity copied onto each captured step.
15
- * @returns Frozen `subSteps`, the number of dropped steps and whether the log was truncated.
16
- */
17
- function captureSubSteps(read, identity) {
18
- const empty = { subSteps: Object.freeze([]), subStepsDropped: 0, subStepsTruncated: false };
19
- if (!read)
20
- return empty;
21
- try {
22
- const logger = (0, synchronous_js_1.synchronous)(read(), 'Debug steps must be synchronous');
23
- return logger === undefined
24
- ? empty
25
- : { ...empty, subSteps: (0, sub_flow_js_1.readSubFlowLogger)(logger, undefined, identity) };
26
- }
27
- catch {
28
- return { ...empty, subStepsDropped: 1 };
29
- }
30
- }