@jangts/agent-core 0.2.1 → 0.3.5

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 (37) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +384 -50
  3. package/development/docs/api/README.en.md +2 -0
  4. package/development/docs/api/README.md +2 -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/reference.md +322 -257
  8. package/development/docs/api/workflow.en.md +10 -0
  9. package/development/docs/api/workflow.md +22 -0
  10. package/development/skills/agent-core-app/SKILL.md +8 -0
  11. package/dist/cjs/execution/cache.js +162 -0
  12. package/dist/cjs/execution/engine.js +10 -0
  13. package/dist/cjs/kits/vector-search.js +4 -7
  14. package/dist/cjs/structs/execution/cache.js +6 -0
  15. package/dist/cjs/support/decorators/definition.js +42 -0
  16. package/dist/cjs/support/decorators/index.js +63 -0
  17. package/dist/cjs/support/decorators/pool.js +87 -0
  18. package/dist/cjs/support/decorators/types.js +2 -0
  19. package/dist/esm/execution/cache.d.ts +20 -0
  20. package/dist/esm/execution/cache.js +156 -0
  21. package/dist/esm/execution/engine.d.ts +5 -0
  22. package/dist/esm/execution/engine.js +10 -0
  23. package/dist/esm/kits/vector-search.js +4 -7
  24. package/dist/esm/structs/execution/cache.d.ts +54 -0
  25. package/dist/esm/structs/execution/cache.js +5 -0
  26. package/dist/esm/structs/execution/engine.d.ts +6 -1
  27. package/dist/esm/structs/execution/workflow.d.ts +2 -1
  28. package/dist/esm/structs/index.d.ts +1 -0
  29. package/dist/esm/support/decorators/definition.d.ts +19 -0
  30. package/dist/esm/support/decorators/definition.js +38 -0
  31. package/dist/esm/support/decorators/index.d.ts +39 -0
  32. package/dist/esm/support/decorators/index.js +48 -0
  33. package/dist/esm/support/decorators/pool.d.ts +97 -0
  34. package/dist/esm/support/decorators/pool.js +83 -0
  35. package/dist/esm/support/decorators/types.d.ts +22 -0
  36. package/dist/esm/support/decorators/types.js +1 -0
  37. package/package.json +11 -3
@@ -186,3 +186,13 @@ infers the output type from the object contract, and the same field also accepts
186
186
  ### Reports during node execution
187
187
 
188
188
  `run(state, context)` can call `context.report({ usedModel, payload, subs })` to report progress. usedModel is optional boolean metadata about this node itself and remains unknown when not reported; payload and subs are read only when content is allowed. subs must be the instance created by `context.createSubFlowLogger()` for this execution, and a hand-written array cannot be submitted. write records the intent first and returns an ID, fix adds later results, and readAll returns an immutable snapshot in a fixed format; writes and completions reuse the existing node.progress, and a short circuit or throw still preserves the start record already sent. Reports do not submit business state and do not determine routing; the Loop adds node identity, time, and event ordinal. Late writes are not accepted after cancellation or node completion. The node's `report(update, context)` at completion continues to serve supplementary reporting when content is allowed. For the contract, see [Child log instances](observability.md#sub-log-instances-and-record-first-then-complete).
189
+
190
+ ## Node caches
191
+
192
+ Engine option `cache: { maxEntries: 1024, ttlMs: 60000 }` configures bounded instance-local LRU memory. Capacity defaults to 1024 across all partitions; omitted TTL means no time-based expiry. Expiry is lazy and measured from writes. Values retain identity, including undefined; has distinguishes a stored undefined from a miss. delete returns a boolean; set and clear return void.
193
+
194
+ context.cache belongs to the current node. Only get(key, { nodeId }) may read another partition. Other operations only address the current partition, and clear never clears the entire Engine. Low-level run(step) uses `$step`. Runs on one Engine share memory; forks preserve bounds but start with empty memory.
195
+
196
+ Optional Engine option agentCache accepts an AgentCachePort. context.agentCache exposes asynchronous get/has/set/delete/clear, or is absent when no port is supplied. The App receives immutable addresses with nodeId, key, persistence and signal; clear receives an address without key. Port callbacks retain their receiver. Cancellation is checked before dispatch and after completion; it does not roll back an App write already started.
197
+
198
+ Persistence defaults to false. Passing true requires supportsPersistence: true; otherwise the call rejects. Volatile and persistent spaces are independent, with no fallback or mirroring. The App owns capacity, TTL, serialization, user isolation and storage. Cache partitions are not authorization boundaries. Fork resume may explicitly return agentCache to restore a borrowed port; it is never inherited implicitly. Cache data is not checkpoint state or durable business evidence, and captured views must not be used after cancellation.
@@ -187,3 +187,25 @@ Loop 将这些补充信息附在节点的完成或跳转记录上。报告失败
187
187
  ### 节点运行中的报告
188
188
 
189
189
  `run(state, context)` 可调用 `context.report({ usedModel, payload, subs })` 报告进度。usedModel 是本次节点自身的可选布尔元数据,未上报保持未知;payload 和 subs 仅在内容获准时读取。subs 必须为 `context.createSubFlowLogger()` 创建的本次执行实例,不能提交手写数组。write 先记意图并返回 ID,fix 补后续结果,readAll 返回固定格式不可变快照;写入和补全复用现有 node.progress,短路或抛错也保留已送出的起始记录。报告不提交业务状态,也不决定路由;Loop 补齐节点身份、时间及事件序号。取消或节点结束后不接纳迟到写入。结束时的节点 `report(update, context)` 继续用于内容获准时的补充报告。合同见[子日志实例](observability.md#子日志实例与先记后补)。
190
+
191
+ ## 节点缓存
192
+
193
+ Engine 构造参数 `cache: { maxEntries: 1024, ttlMs: 60000 }` 控制实例级内存缓存;maxEntries 缺省 1024,ttlMs 省略表示不按时间过期,采用全 Engine 的 LRU 淘汰,不使用后台定时器。值按引用保存,允许存 undefined,has 可区分未命中与已存 undefined。delete 返回是否删除,set/clear 返回 void。
194
+
195
+ 每个节点通过 context.cache 访问自己的分区。get(key, { nodeId }) 可读取其他节点分区;has、set、delete、clear 只能操作当前节点,clear 不清空其他分区。run(step) 使用保留分区 `$step`。同一 Engine 跨 run 共享;fork 保留容量配置但不复制缓存数据。
196
+
197
+ 可选构造参数 agentCache 接收 AgentCachePort。节点的 context.agentCache 是异步视图,未提供端口时不存在。端口用命名 address 接收 nodeId、key、persistence、signal;clear 接收不含 key 的分区地址。端口方法保留原对象 this。所有节点操作前后检查本次取消;取消不会回滚已开始的 App 存储写入。
198
+
199
+ ```ts
200
+ // 在节点 run 中:
201
+ context.cache.set('selection', value);
202
+ const prior = context.cache.get('selection');
203
+ if (context.agentCache) {
204
+ await context.agentCache.set('summary', summary);
205
+ const saved = await context.agentCache.get('summary');
206
+ }
207
+ ```
208
+
209
+ agentCache 每个操作默认 persistence: false;true 必须由 supportsPersistence: true 的 App 端口明确支持,否则拒绝。两种空间不回退、不镜像,delete/clear 仅影响指定空间及当前分区。App 负责容量、TTL、序列化、用户/租户隔离和持久化。节点分区不是授权边界,不复用权限结论。
210
+
211
+ 缓存不进入 checkpoint/状态/Durable 恢复。fork 的 resume 可显式返回 agentCache 重新绑定 App 端口;默认不继承。需要重放或持久业务事实的数据仍使用原 state 补丁或 App 存储。取消后不可继续使用捕获的节点缓存视图。
@@ -70,3 +70,11 @@ Use `docs.acceptance` to select checks for concrete uncertainties, while followi
70
70
  ## Custom model transport boundaries
71
71
 
72
72
  A custom response collector is synchronous and request-local. Its outputChars must be a nonnegative safe integer after creation, each push and finish. An explicit infinite maxOutputChars budget does not permit infinite or invalid collector counts. Core records the largest valid observed count; finish may release buffers. Optional call metadata is read once on a best-effort basis and cannot replace the actual result, original failure or cancellation. Prefer Support transports for standard native calls and JSON; custom transports must preserve complete parsing, budgets and cancellation before any tool execution.
73
+
74
+ ## Node cache ownership
75
+
76
+ Use context.cache for bounded Engine memory; its default capacity is 1024, partitions are node-local and only get may name another node. The low-level step partition is `$step`. An App may explicitly inject an asynchronous AgentCachePort for context.agentCache. Persistence must be supported explicitly, is separate from volatile storage and never falls back. The App owns user isolation, serialization, capacity and expiry; do not cache authorization decisions. Forks start with empty Engine memory and only regain an App port when resume explicitly supplies agentCache. Keep durable facts in state/App storage, not caches.
77
+
78
+ ## Optional definition decorators
79
+
80
+ Use the standard-only /support/decorators entry for class-based definitions or existing host methods. Classes supply original options except id; the decorator constructs once and supplies id. Host methods bind and register automatically during standard instance initialization; read and execute only after successful construction. Constructor failures do not roll back initializer registration. Use long-lived service hosts, never request-scoped ones. Keep helper methods/private fields in their real class, never a duplicate options metadata block or empty carrier. Existing Nest legacy compilation keeps object definitions. No public metadata-plus-function adapters, Engine factories, automatic DI or request lifecycle wrappers are provided. Use nodePool/toolPool/fragmentPool/workflowPool/agentPool/modelPool, without creating pools or manually registering. Decorator ns is a string or string array, default 'default'; same-kind ids are unique within a namespace. Read get(id, ns?), getAll(ns?) or pick(ids, ns?); get may return undefined. Multi-namespace registration shares one definition and preflights all conflicts. ESM/CJS copies share readers within one realm. Read api/decorators.md for registration, receivers and type limits.
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EngineCache = void 0;
4
+ exports.cachePort = cachePort;
5
+ exports.cacheContext = cacheContext;
6
+ const cancellation_js_1 = require("../utils/cancellation.js");
7
+ /** Validate the small cache-specific boundary; business permissions remain App-owned. */
8
+ function textKey(value) {
9
+ if (typeof value !== 'string' || !value.length)
10
+ throw new TypeError('Cache keys must be nonempty strings');
11
+ return value;
12
+ }
13
+ /** Reject accidental cross-partition writes and unknown options instead of ignoring them. */
14
+ function optionsOf(value, allowed) {
15
+ if (value !== undefined &&
16
+ (!value ||
17
+ typeof value !== 'object' ||
18
+ Reflect.ownKeys(value).some((key) => typeof key !== 'string' || !allowed.includes(key))))
19
+ throw new TypeError('Invalid cache options');
20
+ }
21
+ /** Bounded LRU for one Engine; forks get new memory, and values are deliberately not cloned. */
22
+ class EngineCache {
23
+ entries = new Map();
24
+ limit;
25
+ ttl;
26
+ constructor(options = {}) {
27
+ this.limit = options.maxEntries ?? 1024;
28
+ this.ttl = options.ttlMs ?? Infinity;
29
+ if (!Number.isSafeInteger(this.limit) ||
30
+ this.limit < 1 ||
31
+ (this.ttl !== Infinity && (!Number.isSafeInteger(this.ttl) || this.ttl < 1)))
32
+ throw new TypeError('Invalid Engine cache bounds');
33
+ }
34
+ /** Remove expired entries lazily, without timers or background tasks. */
35
+ prune() {
36
+ const now = Date.now();
37
+ for (const [key, item] of this.entries)
38
+ if (item.expires <= now)
39
+ this.entries.delete(key);
40
+ }
41
+ /** Capture an immutable partition and visit signal; no per-run cache is introduced. */
42
+ view(nodeId, signal) {
43
+ textKey(nodeId);
44
+ const prefix = JSON.stringify([nodeId]).slice(0, -1) + ',';
45
+ const address = (key, node = nodeId) => JSON.stringify([textKey(node), textKey(key)]);
46
+ const read = (key, node = nodeId) => {
47
+ signal.throwIfAborted();
48
+ const id = address(key, node);
49
+ const item = this.entries.get(id);
50
+ if (item && item.expires <= Date.now()) {
51
+ this.entries.delete(id);
52
+ return undefined;
53
+ }
54
+ if (item) {
55
+ this.entries.delete(id);
56
+ this.entries.set(id, item);
57
+ }
58
+ return item;
59
+ };
60
+ return Object.freeze({
61
+ get: (key, options) => {
62
+ optionsOf(options, ['nodeId']);
63
+ return read(key, options?.nodeId)?.value;
64
+ },
65
+ has: (key) => read(key) !== undefined,
66
+ set: (key, value) => {
67
+ signal.throwIfAborted();
68
+ const id = address(key);
69
+ this.prune();
70
+ this.entries.delete(id);
71
+ this.entries.set(id, { value, expires: Date.now() + this.ttl });
72
+ if (this.entries.size > this.limit)
73
+ this.entries.delete(this.entries.keys().next().value);
74
+ },
75
+ delete: (key) => {
76
+ signal.throwIfAborted();
77
+ return this.entries.delete(address(key));
78
+ },
79
+ clear: () => {
80
+ signal.throwIfAborted();
81
+ for (const key of this.entries.keys())
82
+ if (key.startsWith(prefix))
83
+ this.entries.delete(key);
84
+ },
85
+ });
86
+ }
87
+ }
88
+ exports.EngineCache = EngineCache;
89
+ /** Snapshot port callbacks with their receiver, rejecting incomplete App storage early. */
90
+ function cachePort(port) {
91
+ if (port === undefined)
92
+ return undefined;
93
+ const handlers = ['get', 'has', 'set', 'delete', 'clear'];
94
+ if (!port ||
95
+ handlers.some((key) => typeof port[key] !== 'function') ||
96
+ (port.supportsPersistence !== undefined && typeof port.supportsPersistence !== 'boolean'))
97
+ throw new TypeError('Invalid Agent cache port');
98
+ return Object.freeze({
99
+ supportsPersistence: port.supportsPersistence === true,
100
+ get: port.get.bind(port),
101
+ has: port.has.bind(port),
102
+ set: port.set.bind(port),
103
+ delete: port.delete.bind(port),
104
+ clear: port.clear.bind(port),
105
+ });
106
+ }
107
+ /** Build visit-local views. Persistent writes remain external side effects owned by the App. */
108
+ function cacheContext(memory, port, nodeId, signal) {
109
+ const partition = (options, crossRead = false) => {
110
+ signal.throwIfAborted();
111
+ optionsOf(options, crossRead ? ['nodeId', 'persistence'] : ['persistence']);
112
+ const persistence = options?.persistence ?? false;
113
+ if (typeof persistence !== 'boolean')
114
+ throw new TypeError('Invalid cache persistence');
115
+ if (persistence && !port?.supportsPersistence)
116
+ throw new TypeError('Agent cache persistence is unsupported');
117
+ return Object.freeze({
118
+ nodeId: textKey(crossRead ? (options?.nodeId ?? nodeId) : nodeId),
119
+ persistence,
120
+ signal,
121
+ });
122
+ };
123
+ const address = (key, options, crossRead = false) => Object.freeze({ ...partition(options, crossRead), key: textKey(key) });
124
+ const call = async (invoke) => {
125
+ signal.throwIfAborted();
126
+ const value = await (0, cancellation_js_1.awaitWithSignal)(invoke(), signal);
127
+ signal.throwIfAborted();
128
+ return value;
129
+ };
130
+ return {
131
+ cache: memory.view(nodeId, signal),
132
+ ...(port
133
+ ? {
134
+ agentCache: Object.freeze({
135
+ get: (key, options) => call(() => port.get(address(key, options, true))),
136
+ has: (key, options) => call(async () => {
137
+ const result = await port.has(address(key, options));
138
+ if (typeof result !== 'boolean')
139
+ throw new TypeError('Agent cache has must return boolean');
140
+ return result;
141
+ }),
142
+ set: (key, value, options) => call(async () => {
143
+ const result = await port.set(address(key, options), value);
144
+ if (result !== undefined)
145
+ throw new TypeError('Agent cache set must return void');
146
+ }),
147
+ delete: (key, options) => call(async () => {
148
+ const result = await port.delete(address(key, options));
149
+ if (typeof result !== 'boolean')
150
+ throw new TypeError('Agent cache delete must return boolean');
151
+ return result;
152
+ }),
153
+ clear: (options) => call(async () => {
154
+ const result = await port.clear(partition(options));
155
+ if (result !== undefined)
156
+ throw new TypeError('Agent cache clear must return void');
157
+ }),
158
+ }),
159
+ }
160
+ : {}),
161
+ };
162
+ }
@@ -26,6 +26,7 @@ exports.AgentCoreEngine = void 0;
26
26
  *
27
27
  * Exports: `AgentCoreEngine` and the engine option/identity types.
28
28
  */
29
+ const cache_js_1 = require("./cache.js");
29
30
  const call_budget_js_1 = require("../model/call-budget.js");
30
31
  const runner_js_1 = require("../model/runner.js");
31
32
  const binding_js_1 = require("../model/binding.js");
@@ -56,6 +57,8 @@ class AgentCoreEngine {
56
57
  static AttemptBudget = workflow_js_1.AttemptBudget;
57
58
  /** Factory for a validated workflow definition. */
58
59
  static createWorkflow = workflow_js_1.defineWorkflow;
60
+ cacheMemory;
61
+ serviceCache;
59
62
  maxRounds;
60
63
  exhausted;
61
64
  options;
@@ -306,6 +309,8 @@ class AgentCoreEngine {
306
309
  (0, rules_js_1.breakWhile)(!!this.invokeTool && !dependencies?.tools, 'Fork requires a restored tool runtime');
307
310
  const engine = new AgentCoreEngine({
308
311
  maxRounds: this.maxRounds,
312
+ cache: this.options.cache,
313
+ agentCache: dependencies?.agentCache,
309
314
  errors: { exhausted: this.exhausted },
310
315
  execution: {
311
316
  diagnostics: this.options.execution?.diagnostics,
@@ -350,6 +355,8 @@ class AgentCoreEngine {
350
355
  ['maxSteps', 'exhausted', 'signal', 'diagnostics', 'onWorkflowEvent', 'observe'].some((key) => key in settings)) {
351
356
  throw new TypeError('Use maxRounds, execution (including observe) and errors');
352
357
  }
358
+ this.cacheMemory = new cache_js_1.EngineCache(settings.cache);
359
+ this.serviceCache = (0, cache_js_1.cachePort)(settings.agentCache);
353
360
  const execution = settings.execution;
354
361
  if (settings.branch !== undefined)
355
362
  this.branchIdentity = (0, branch_js_1.snapshotBranch)(settings.branch);
@@ -359,6 +366,7 @@ class AgentCoreEngine {
359
366
  settings.errors?.exhausted?.bind(settings.errors) ??
360
367
  (() => new errors_js_2.BudgetError('Agent round budget exhausted'));
361
368
  this.options = {
369
+ cache: { ...settings.cache },
362
370
  execution: {
363
371
  signal: execution?.signal,
364
372
  diagnostics: execution?.diagnostics,
@@ -478,6 +486,7 @@ class AgentCoreEngine {
478
486
  phase = 'run';
479
487
  signal.throwIfAborted();
480
488
  const result = await (0, cancellation_js_1.awaitWithSignal)(step(index, {
489
+ ...(0, cache_js_1.cacheContext)(this.cacheMemory, this.serviceCache, '$step', signal),
481
490
  signal,
482
491
  diagnostics,
483
492
  report: reports.report.bind(reports),
@@ -673,6 +682,7 @@ class AgentCoreEngine {
673
682
  });
674
683
  const reports = new reporting_js_1.NodeReports(nodeDiagnostics, signal);
675
684
  const context = {
685
+ ...(0, cache_js_1.cacheContext)(this.cacheMemory, this.serviceCache, node.id, signal),
676
686
  report: reports.report.bind(reports),
677
687
  createSubFlowLogger: reports.createSubFlowLogger.bind(reports),
678
688
  roundIndex: round,
@@ -80,11 +80,8 @@ class VectorSearch {
80
80
  (!Number.isFinite(options.minScore) || options.minScore < -1 || options.minScore > 1)), 'Invalid vector query');
81
81
  const query = normalize(vector, this.space.dimensions), groups = new Map();
82
82
  for (const [id, { document, vector: value }] of this.entries) {
83
- // One isolated snapshot per candidate serves filter, grouping and the
84
- // returned hit; filters and groupers are typed readonly, so sharing it
85
- // removes the redundant clones without exposing the index.
86
- const candidate = structuredClone(document);
87
- const allowed = (0, synchronous_js_1.booleanResult)(options.filter(candidate), 'Vector filters must be synchronous', 'Vector filters must return boolean');
83
+ // Each callback receives its own snapshot; readonly types cannot prevent runtime mutation.
84
+ const allowed = (0, synchronous_js_1.booleanResult)(options.filter(structuredClone(document)), 'Vector filters must be synchronous', 'Vector filters must return boolean');
88
85
  if (!allowed)
89
86
  continue;
90
87
  let score = 0;
@@ -93,12 +90,12 @@ class VectorSearch {
93
90
  score = Math.max(-1, Math.min(1, score));
94
91
  if (score < (options.minScore ?? -1))
95
92
  continue;
96
- const group = (0, synchronous_js_1.synchronous)(options.groupBy?.(candidate), 'Vector grouping must be synchronous') ?? id;
93
+ const group = (0, synchronous_js_1.synchronous)(options.groupBy?.(structuredClone(document)), 'Vector grouping must be synchronous') ?? id;
97
94
  if (typeof group !== 'string')
98
95
  throw new TypeError('Vector groups must be strings');
99
96
  const prior = groups.get(group);
100
97
  if (!prior || score > prior.score || (score === prior.score && id < prior.id))
101
- groups.set(group, { id, score, document: candidate });
98
+ groups.set(group, { id, score, document: structuredClone(document) });
102
99
  }
103
100
  return [...groups.values()]
104
101
  .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Node-partitioned cache contracts. Engine memory is synchronous; service-owned storage is
4
+ * asynchronous and explicitly selects volatile or persistent space. Neither is workflow state.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classDecoration = classDecoration;
4
+ exports.methodDecoration = methodDecoration;
5
+ /**
6
+ * Internal decoration: construct definition classes or bind methods to their real host,
7
+ * compile through existing entry points and automatically register in the kind's root.
8
+ */
9
+ const pool_js_1 = require("./pool.js");
10
+ /** Restore only the decorator-owned id, preserving instance identity, getters and private state. */
11
+ function withId(id, instance) {
12
+ if ('id' in instance)
13
+ throw new TypeError('Decoration supplies id; do not also define it on the class');
14
+ Object.defineProperty(instance, 'id', { value: id, enumerable: true });
15
+ return instance;
16
+ }
17
+ /** Class definitions register once after static initialization and successful construction. */
18
+ function classDecoration(kind, id, options, compile) {
19
+ return (_target, context) => {
20
+ if (context?.kind !== 'class')
21
+ throw new TypeError('Use standard class decorator semantics');
22
+ context.addInitializer(function () {
23
+ (0, pool_js_1.registerDefinition)(kind, compile(withId(id, new this())), options.ns);
24
+ });
25
+ };
26
+ }
27
+ /** Method initializers bind and register automatically; no scanning or caller-side bind is needed. */
28
+ function methodDecoration(kind, id, metadata, handlerKey, options, compile) {
29
+ return (method, context) => {
30
+ if (context?.kind !== 'method' || context.static || context.private)
31
+ throw new TypeError('Use a standard public instance method decorator');
32
+ context.addInitializer(function () {
33
+ const value = { ...metadata, [handlerKey]: method.bind(this) };
34
+ for (const key of Reflect.ownKeys(value)) {
35
+ const member = value[key];
36
+ if (typeof member === 'function')
37
+ value[key] = member.bind(this);
38
+ }
39
+ (0, pool_js_1.registerDefinition)(kind, compile(withId(id, value)), options.ns);
40
+ });
41
+ };
42
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modelPool = exports.agentPool = exports.workflowPool = exports.fragmentPool = exports.toolPool = exports.nodePool = void 0;
4
+ exports.node = node;
5
+ exports.tool = tool;
6
+ exports.fragment = fragment;
7
+ exports.workflow = workflow;
8
+ exports.agent = agent;
9
+ exports.model = model;
10
+ const tools_js_1 = require("../tools.js");
11
+ const workflow_js_1 = require("../../execution/workflow.js");
12
+ const assembly_js_1 = require("../context/assembly.js");
13
+ const binding_js_1 = require("../../model/binding.js");
14
+ const definition_js_1 = require("./definition.js");
15
+ var pool_js_1 = require("./pool.js");
16
+ Object.defineProperty(exports, "nodePool", { enumerable: true, get: function () { return pool_js_1.nodePool; } });
17
+ Object.defineProperty(exports, "toolPool", { enumerable: true, get: function () { return pool_js_1.toolPool; } });
18
+ Object.defineProperty(exports, "fragmentPool", { enumerable: true, get: function () { return pool_js_1.fragmentPool; } });
19
+ Object.defineProperty(exports, "workflowPool", { enumerable: true, get: function () { return pool_js_1.workflowPool; } });
20
+ Object.defineProperty(exports, "agentPool", { enumerable: true, get: function () { return pool_js_1.agentPool; } });
21
+ Object.defineProperty(exports, "modelPool", { enumerable: true, get: function () { return pool_js_1.modelPool; } });
22
+ /** Internal conversion shared by node/tool/fragment; never a public free-function adapter. */
23
+ function decorate(kind, id, options, key, compile) {
24
+ return (value, context) => {
25
+ if (context?.kind === 'class') {
26
+ (0, definition_js_1.classDecoration)(kind, id, options, compile)(value, context);
27
+ }
28
+ else {
29
+ const { ns, ...metadata } = options;
30
+ (0, definition_js_1.methodDecoration)(kind, id, metadata, key, { ns }, compile)(value, context);
31
+ }
32
+ };
33
+ }
34
+ function node(id, options = {}) {
35
+ return decorate('node', id, options, 'run', (value) => value);
36
+ }
37
+ function tool(id, options = {}) {
38
+ return decorate('tool', id, options, 'execute', (value) => (0, tools_js_1.defineTool)(value));
39
+ }
40
+ function fragment(id, options = {}) {
41
+ return decorate('fragment', id, options, 'format', (value) => {
42
+ (0, assembly_js_1.defineContextFragments)([value]);
43
+ return value;
44
+ });
45
+ }
46
+ /** Instantiate and validate workflow options, leaving actual execution with the original Engine. */
47
+ function workflow(id, options = {}) {
48
+ return (0, definition_js_1.classDecoration)('workflow', id, options, (value) => {
49
+ (0, workflow_js_1.defineWorkflow)(value);
50
+ return value;
51
+ });
52
+ }
53
+ /** Instantiate an Engine configuration class without creating an Engine, budget or request. */
54
+ function agent(id, options = {}) {
55
+ return (0, definition_js_1.classDecoration)('agent', id, options, (value) => value);
56
+ }
57
+ /** Instantiate and validate model options without creating a Runner or consuming calls. */
58
+ function model(id, options = {}) {
59
+ return (0, definition_js_1.classDecoration)('model', id, options, (value) => {
60
+ (0, binding_js_1.snapshotModelBinding)(value);
61
+ return value;
62
+ });
63
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modelPool = exports.agentPool = exports.workflowPool = exports.fragmentPool = exports.toolPool = exports.nodePool = exports.definitionKinds = void 0;
4
+ exports.registerDefinition = registerDefinition;
5
+ /**
6
+ * One process-realm definition registry per kind. The shared symbol makes ESM/CJS copies
7
+ * consume the same stores; consumers receive frozen read-only views, never pool factories.
8
+ */
9
+ const protocol_registry_js_1 = require("../../utils/protocol-registry.js");
10
+ /** Enumerable categories drive allocation and lookup; no additional runtime dispatch layer. */
11
+ exports.definitionKinds = ['node', 'tool', 'fragment', 'workflow', 'agent', 'model'];
12
+ const sharedKey = Symbol.for('@jangts/agent-core/decorator-roots/v2');
13
+ const realm = globalThis;
14
+ /** Allocate once per realm, including when another module format initialized the roots first. */
15
+ function sharedPools() {
16
+ const existing = realm[sharedKey];
17
+ if (existing) {
18
+ if (existing.version !== 2 || exports.definitionKinds.some((kind) => !existing.stores[kind]))
19
+ throw new TypeError('Incompatible decorator root registry');
20
+ return existing;
21
+ }
22
+ const stores = Object.fromEntries(exports.definitionKinds.map((kind) => [kind, new Map()]));
23
+ const pools = Object.freeze({ version: 2, stores: Object.freeze(stores) });
24
+ Object.defineProperty(globalThis, sharedKey, { value: pools });
25
+ return pools;
26
+ }
27
+ /** Namespace is an exact, nonblank key; no trimming, fallback or hierarchy is applied. */
28
+ function namespace(value) {
29
+ if (typeof value !== 'string' || !value.trim())
30
+ throw new TypeError('Decorator namespace must be a nonempty string');
31
+ return value;
32
+ }
33
+ /** Preflight every destination so a conflict never leaves partial multi-namespace registration. */
34
+ function registerDefinition(kind, definition, ns = 'default') {
35
+ if (typeof definition.id !== 'string' || !definition.id.trim())
36
+ throw new TypeError('Invalid decorator definition id');
37
+ const requested = typeof ns === 'string' ? [ns] : ns;
38
+ if (!Array.isArray(requested) || requested.length === 0)
39
+ throw new TypeError('Decorator ns must be a namespace or a nonempty namespace array');
40
+ const names = [...new Set(requested.map(namespace))];
41
+ const store = sharedPools().stores[kind];
42
+ for (const name of names) {
43
+ if (store.get(name)?.has(definition.id))
44
+ throw new Error(`Duplicate ${kind} definition: ${definition.id} (ns: ${name})`);
45
+ }
46
+ for (const name of names) {
47
+ let entries = store.get(name);
48
+ if (!entries)
49
+ store.set(name, (entries = new protocol_registry_js_1.ProtocolRegistry()));
50
+ entries.set(definition.id, definition);
51
+ }
52
+ }
53
+ /** Root reads never instantiate or execute definitions; array results are immutable snapshots. */
54
+ function reader(kind) {
55
+ const store = sharedPools().stores[kind];
56
+ return Object.freeze({
57
+ get(id, ns = 'default') {
58
+ return store.get(namespace(ns))?.get(id);
59
+ },
60
+ getAll(ns = 'default') {
61
+ return Object.freeze([...(store.get(namespace(ns))?.values() ?? [])]);
62
+ },
63
+ pick(ids, ns = 'default') {
64
+ const entries = store.get(namespace(ns));
65
+ const selected = new protocol_registry_js_1.ProtocolRegistry();
66
+ for (const id of ids) {
67
+ const definition = entries?.get(id);
68
+ if (!definition)
69
+ throw new TypeError(`Unknown ${kind} definition: ${id} (ns: ${ns})`);
70
+ selected.set(id, definition);
71
+ }
72
+ return Object.freeze([...selected.values()]);
73
+ },
74
+ });
75
+ }
76
+ /** Decorated nodes; ids are unique within each namespace. */
77
+ exports.nodePool = reader('node');
78
+ /** Decorated tools; namespaces isolate otherwise identical ids. */
79
+ exports.toolPool = reader('tool');
80
+ /** Decorated fragment configurations; use the original builder to assemble them. */
81
+ exports.fragmentPool = reader('fragment');
82
+ /** Decorated workflow definitions; execution still belongs to the original Engine. */
83
+ exports.workflowPool = reader('workflow');
84
+ /** Decorated Engine options; request-specific App contracts are supplied on typed reads. */
85
+ exports.agentPool = reader('agent');
86
+ /** Decorated Runner options; reading does not create a Runner or spend model calls. */
87
+ exports.modelPool = reader('model');
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Engine memory and borrowed service storage views. Partition ownership is captured per visit,
3
+ * never in a mutable service field. Cache calls do not create model budgets or execute tools.
4
+ */
5
+ import type { AgentCachePort, CacheContext, EngineCacheOptions, NodeCache } from '../structs/execution/cache.js';
6
+ /** Bounded LRU for one Engine; forks get new memory, and values are deliberately not cloned. */
7
+ export declare class EngineCache {
8
+ private readonly entries;
9
+ private readonly limit;
10
+ private readonly ttl;
11
+ constructor(options?: EngineCacheOptions);
12
+ /** Remove expired entries lazily, without timers or background tasks. */
13
+ private prune;
14
+ /** Capture an immutable partition and visit signal; no per-run cache is introduced. */
15
+ view(nodeId: string, signal: AbortSignal): NodeCache;
16
+ }
17
+ /** Snapshot port callbacks with their receiver, rejecting incomplete App storage early. */
18
+ export declare function cachePort(port?: AgentCachePort): AgentCachePort | undefined;
19
+ /** Build visit-local views. Persistent writes remain external side effects owned by the App. */
20
+ export declare function cacheContext(memory: EngineCache, port: AgentCachePort | undefined, nodeId: string, signal: AbortSignal): CacheContext;