Skip to content
中文

Decorators: complete handlers and existing host methods ​

Classes construct definitions automatically; methods bind the real host. Outputs still use original define, constructor and bind contracts.

Import only from /support/decorators with standard decorator semantics; legacy NestJS experimentalDecorators units are unsupported. No new dependencies, reflection or automatic DI are introduced.

The decorator supplies id while the class owns configuration, its handler and private helper methods. Named Node/Tool/Fragment/Workflow/Agent/Model contracts omit id. Classes require zero-argument construction and are constructed once.

Each kind has one shared read-only pool. Decorator ns accepts a string or string array and defaults to default. Ids are unique within a kind and namespace; other namespaces may reuse them. Multi-namespace registration shares one object and preflights all conflicts. Read with get(id, ns?), getAll(ns?) or pick(ids, ns?); missing get returns undefined, while pick rejects missing or repeated ids. Arrays are frozen snapshots.

Methods bind and register during standard instance initialization. Read and execute after successful host construction. Failed construction does not roll back completed registration and is a wiring failure. Use long-lived hosts, not request-scoped instances; duplicate kind/namespace/id rejects. No registerFrom or manual bind is needed.

node adapts run, tool execute and fragment format, retaining original checks. agent/model define options without creating Engines/Runners. ns stays outside Core options and ids remain unchanged. Original graph and tool uniqueness checks still apply when combining namespaces. ESM/CJS copies share roots within one realm; processes and workers do not. Reading is neither execution nor authorization.

Progressive examples for the same business case ​

A class owns configuration and private helpers without manual construction ​

Beginner · TypeScript 标准装饰器

ts
import { AgentCoreEngine, defineWorkflow } from '@jangts/agent-core';
import type { WorkflowNode } from '@jangts/agent-core';
import { node, nodePool } from '@jangts/agent-core/support/decorators';
import type { Node } from '@jangts/agent-core/support/decorators';
type State = { text: string; answer?: string };
@node<State, string>('greet', { ns: 'greeting' })
class Greeting implements Node<State, string> {
  readonly description = '生成问候';
  readonly routes = [{ complete: (state: Readonly<State>) => state.answer! }];
  #prefix = '你好,';
  run(state: Readonly<State>) { return { answer: this._format(state.text) }; }
  private _format(text: string) { return this.#prefix + text.trim(); }
}
const flow = defineWorkflow<State, string>({ id: 'greeting', nodes: nodePool.getAll('greeting') });
console.log(await new AgentCoreEngine({ workflow: flow }).run({ text: '访客' }));
js
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { AgentCoreEngine, defineWorkflow } from '@jangts/agent-core';
import { node, nodePool } from '@jangts/agent-core/support/decorators';
let Greeting = class Greeting {
    description = '生成问候';
    routes = [{ complete: (state) => state.answer }];
    #prefix = '你好,';
    run(state) { return { answer: this._format(state.text) }; }
    _format(text) { return this.#prefix + text.trim(); }
};
Greeting = __decorate([
    node('greet', { ns: 'greeting' })
], Greeting);
const flow = defineWorkflow({ id: 'greeting', nodes: nodePool.getAll('greeting') });
console.log(await new AgentCoreEngine({ workflow: flow }).run({ text: '访客' }));

Keep a service method name and bind its original host automatically ​

Configuration · TypeScript 标准装饰器

ts
import type { WorkflowNode } from '@jangts/agent-core';
import { node, nodePool } from '@jangts/agent-core/support/decorators';
type State = { text: string; answer?: string };
class GreetingService {
  constructor(private readonly prefix: string) {}
  @node<State, string>('reply', { ns: 'service',
    description: '调用现有服务',
    routes: [{ complete: (state: Readonly<State>) => state.answer! }],
  })
  answer(state: Readonly<State>) { return { answer: this.prefix + state.text }; }
}
const service = new GreetingService('你好,');
const definition = nodePool.get<WorkflowNode<State, string>>('reply', 'service')!;
console.log(definition === nodePool.get('reply', 'service'));
js
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { node, nodePool } from '@jangts/agent-core/support/decorators';
class GreetingService {
    prefix;
    constructor(prefix) {
        this.prefix = prefix;
    }
    answer(state) { return { answer: this.prefix + state.text }; }
}
__decorate([
    node('reply', { ns: 'service',
        description: '调用现有服务',
        routes: [{ complete: (state) => state.answer }],
    })
], GreetingService.prototype, "answer", null);
const service = new GreetingService('你好,');
const definition = nodePool.get('reply', 'service');
console.log(definition === nodePool.get('reply', 'service'));

Tool classes preserve original authorization and result checks ​

Extension · TypeScript 标准装饰器

ts
import { Support } from '@jangts/agent-core';
import type { ToolDefinition } from '@jangts/agent-core';
import { tool, toolPool } from '@jangts/agent-core/support/decorators';
import type { Tool } from '@jangts/agent-core/support/decorators';
type Input = { text: string };
@tool<Input, string, undefined>('echo', { ns: 'echo' })
class Echo implements Tool<Input, string, undefined> {
  readonly kind = 'read';
  readonly description = '回显文本';
  readonly inputSchema = {
    jsonSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
    parse(value: unknown): Input {
      if (!value || typeof value !== 'object' || !('text' in value) || typeof value.text !== 'string') throw new TypeError('text');
      return { text: value.text };
    },
  };
  authorize(): void {}
  execute(input: Input) { return this._format(input.text); }
  private _format(text: string) { return text.trim(); }
  parseOutput(value: unknown): string {
    if (typeof value !== 'string') throw new TypeError('string');
    return value;
  }
}
const runtime = Support.defineToolCatalog<undefined>(toolPool.getAll<ToolDefinition<Input, string, undefined>>('echo')).bind({ app: undefined });
console.log((await runtime.invoke({ toolId: 'echo', input: { text: 'hello' } })).value);
js
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Support } from '@jangts/agent-core';
import { tool, toolPool } from '@jangts/agent-core/support/decorators';
let Echo = class Echo {
    kind = 'read';
    description = '回显文本';
    inputSchema = {
        jsonSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
        parse(value) {
            if (!value || typeof value !== 'object' || !('text' in value) || typeof value.text !== 'string')
                throw new TypeError('text');
            return { text: value.text };
        },
    };
    authorize() { }
    execute(input) { return this._format(input.text); }
    _format(text) { return text.trim(); }
    parseOutput(value) {
        if (typeof value !== 'string')
            throw new TypeError('string');
        return value;
    }
};
Echo = __decorate([
    tool('echo', { ns: 'echo' })
], Echo);
const runtime = Support.defineToolCatalog(toolPool.getAll('echo')).bind({ app: undefined });
console.log((await runtime.invoke({ toolId: 'echo', input: { text: 'hello' } })).value);

Fragments, workflows and runtime options return to existing entry points ​

Extension · TypeScript 标准装饰器

ts
import { AgentCoreEngine, ModelRunner, Support, defineWorkflow } from '@jangts/agent-core';
import type { AgentWorkflowDefinition, ContextFragmentConfig } from '@jangts/agent-core/types';
import { fragment, workflow, agent, model, fragmentPool, workflowPool, agentPool, modelPool } from '@jangts/agent-core/support/decorators';
import type { AgentOptions, ModelOptions } from '@jangts/agent-core/support/decorators';
@fragment<string>('subject')
class Subject {
  readonly sourceId = 'app'; readonly trust = 'server' as const;
  readonly lifetime = 'turn' as const; readonly role = 'user' as const; readonly maxChars = 100;
  format(text: string) { return text; }
}
const builder = Support.defineContextFragments([fragmentPool.get<ContextFragmentConfig<string>>('subject')!]);
console.log(builder.fragments('hello'));
@workflow<{ answer: string }, string>('flow')
class Flow {
  readonly nodes = [{ id: 'done', description: '交付', routes: [{ complete: (s: {answer: string}) => s.answer }] }];
}
const flow = defineWorkflow(workflowPool.get<AgentWorkflowDefinition<{answer:string},string>>('flow')!);
@agent<{answer:string}, string>('configured')
class Configured { readonly workflow = flow; }
console.log(await new AgentCoreEngine<string,{answer:string}>(agentPool.get<AgentOptions<{answer:string},string>>('configured')!).run({answer:'hello'}));
@model('text-model')
class TextModel { readonly provider = { async *stream() { yield 'hello'; } }; readonly maxCalls = 1; }
const runner = new ModelRunner(modelPool.get<ModelOptions>('text-model')!);
console.log((await runner.invoke({input:'hello'})).value);
js
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { AgentCoreEngine, ModelRunner, Support, defineWorkflow } from '@jangts/agent-core';
import { fragment, workflow, agent, model, fragmentPool, workflowPool, agentPool, modelPool } from '@jangts/agent-core/support/decorators';
let Subject = class Subject {
    sourceId = 'app';
    trust = 'server';
    lifetime = 'turn';
    role = 'user';
    maxChars = 100;
    format(text) { return text; }
};
Subject = __decorate([
    fragment('subject')
], Subject);
const builder = Support.defineContextFragments([fragmentPool.get('subject')]);
console.log(builder.fragments('hello'));
let Flow = class Flow {
    nodes = [{ id: 'done', description: '交付', routes: [{ complete: (s) => s.answer }] }];
};
Flow = __decorate([
    workflow('flow')
], Flow);
const flow = defineWorkflow(workflowPool.get('flow'));
let Configured = class Configured {
    workflow = flow;
};
Configured = __decorate([
    agent('configured')
], Configured);
console.log(await new AgentCoreEngine(agentPool.get('configured')).run({ answer: 'hello' }));
let TextModel = class TextModel {
    provider = { async *stream() { yield 'hello'; } };
    maxCalls = 1;
};
TextModel = __decorate([
    model('text-model')
], TextModel);
const runner = new ModelRunner(modelPool.get('text-model'));
console.log((await runner.invoke({ input: 'hello' })).value);

API reference, usage examples and developer guides.