@jangts/agent-core 0.3.6 → 0.5.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.
Files changed (214) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +5 -5
  3. package/development/docs/README.en.md +2 -2
  4. package/development/docs/acceptance.en.md +2 -2
  5. package/development/docs/agent-development.en.md +1 -1
  6. package/development/docs/api/README.en.md +4 -2
  7. package/development/docs/api/README.md +2 -0
  8. package/development/docs/api/configuration-in-workflow.en.md +403 -0
  9. package/development/docs/api/configuration-in-workflow.md +403 -0
  10. package/development/docs/api/core.en.md +7 -7
  11. package/development/docs/api/core.md +6 -6
  12. package/development/docs/api/decorators.en.md +20 -0
  13. package/development/docs/api/decorators.md +20 -0
  14. package/development/docs/api/examples.en.md +1 -1
  15. package/development/docs/api/examples.md +1 -1
  16. package/development/docs/api/kits.en.md +22 -4
  17. package/development/docs/api/kits.md +22 -4
  18. package/development/docs/api/knowledge-answering.en.md +4 -1
  19. package/development/docs/api/knowledge-answering.md +4 -1
  20. package/development/docs/api/mock.en.md +2 -0
  21. package/development/docs/api/mock.md +2 -0
  22. package/development/docs/api/reference.en.md +508 -410
  23. package/development/docs/api/reference.md +508 -410
  24. package/development/docs/api/support.en.md +55 -9
  25. package/development/docs/api/support.md +53 -7
  26. package/development/docs/api/types.en.md +35 -1
  27. package/development/docs/api/types.md +43 -0
  28. package/development/docs/basic-usage.en.md +1 -1
  29. package/development/docs/basic-usage.md +1 -1
  30. package/development/docs/create-app.en.md +1 -1
  31. package/development/docs/examples/registration/associated.mjs +8 -0
  32. package/development/docs/examples/registration/independent.mjs +20 -0
  33. package/development/docs/examples/registration/protocol.mjs +48 -0
  34. package/development/docs/examples/registration/run.mjs +47 -0
  35. package/development/docs/examples/registration/tools.mjs +75 -0
  36. package/development/docs/examples/registration/workflow.mjs +109 -0
  37. package/development/docs/flows/knowledge-maintenance.en.md +1 -1
  38. package/development/docs/materials.en.md +1 -1
  39. package/development/docs/mechanisms.en.md +2 -2
  40. package/development/docs/mechanisms.md +1 -1
  41. package/development/docs/operations.en.md +1 -1
  42. package/development/docs/records.en.md +1 -1
  43. package/development/docs/registration.en.md +4 -4
  44. package/development/docs/registration.md +2 -2
  45. package/development/skills/agent-core-app/SKILL.md +4 -4
  46. package/development/skills/agent-core-app/templates/node-ts/README.md.tpl +1 -1
  47. package/development/skills/agent-core-app/templates/node-ts/src/catalog.ts.tpl +2 -2
  48. package/development/skills/agent-core-app/templates/node-ts/test/app.test.mjs.tpl +1 -1
  49. package/dist/cjs/capabilities/index.js +5 -12
  50. package/dist/cjs/capabilities/model.js +56 -12
  51. package/dist/cjs/capabilities/tool-conditions.js +65 -0
  52. package/dist/cjs/capabilities/tool-runtime.js +10 -3
  53. package/dist/cjs/context/audit.js +5 -8
  54. package/dist/cjs/context/index.js +3 -4
  55. package/dist/cjs/index.js +21 -17
  56. package/dist/cjs/kits/answering/tasks.js +5 -5
  57. package/dist/cjs/kits/domains/catalog.js +113 -0
  58. package/dist/cjs/kits/domains/index.js +20 -0
  59. package/dist/cjs/kits/{intents → domains}/registry.js +77 -31
  60. package/dist/cjs/kits/{intents → domains}/scopes.js +21 -21
  61. package/dist/cjs/kits/index.js +14 -13
  62. package/dist/cjs/kits/presentation/formats.js +110 -0
  63. package/dist/cjs/kits/presentation/index.js +10 -5
  64. package/dist/cjs/kits/reflection.js +2 -2
  65. package/dist/cjs/kits/text-blocks.js +101 -0
  66. package/dist/cjs/kits/tools.js +246 -158
  67. package/dist/cjs/model/index.js +2 -6
  68. package/dist/cjs/model/interactions/calls.js +61 -0
  69. package/dist/cjs/model/interactions/json-object.js +11 -11
  70. package/dist/cjs/model/interactions/native-functions.js +17 -23
  71. package/dist/cjs/model/message-diagnostics.js +13 -3
  72. package/dist/cjs/model/options.js +2 -0
  73. package/dist/cjs/model/preparation.js +39 -6
  74. package/dist/cjs/model/runner.js +14 -0
  75. package/dist/cjs/model/selection-transport.js +4 -4
  76. package/dist/cjs/model/token-budget.js +3 -0
  77. package/dist/cjs/model/transports.js +3 -2
  78. package/dist/cjs/structs/index.js +7 -6
  79. package/dist/cjs/structs/kits/presentation/formats.js +2 -0
  80. package/dist/cjs/structs/kits/text-blocks.js +11 -0
  81. package/dist/cjs/structs/model/session.js +11 -0
  82. package/dist/cjs/structs/support/associations.js +2 -0
  83. package/dist/cjs/support/associations.js +79 -0
  84. package/dist/cjs/support/context/snapshot.js +11 -1
  85. package/dist/cjs/support/decorators/index.js +45 -3
  86. package/dist/cjs/support/index.js +1 -0
  87. package/dist/cjs/support/model/capabilities.js +35 -5
  88. package/dist/cjs/support/model/chat-completions.js +11 -2
  89. package/dist/cjs/support/model/completion/calls.js +10 -56
  90. package/dist/cjs/support/model/preview-rules.js +11 -11
  91. package/dist/cjs/support/model/reply-preview.js +3 -3
  92. package/dist/cjs/support/model/session.js +152 -0
  93. package/dist/cjs/support/model/stage-transport.js +8 -8
  94. package/dist/cjs/support/model/transports.js +5 -2
  95. package/dist/cjs/support/tools.js +42 -17
  96. package/dist/esm/capabilities/index.d.ts +5 -10
  97. package/dist/esm/capabilities/index.js +5 -9
  98. package/dist/esm/capabilities/model.d.ts +17 -13
  99. package/dist/esm/capabilities/model.js +54 -10
  100. package/dist/esm/capabilities/tool-conditions.d.ts +9 -0
  101. package/dist/esm/capabilities/tool-conditions.js +59 -0
  102. package/dist/esm/capabilities/tool-runtime.js +10 -3
  103. package/dist/esm/capabilities/tools.d.ts +3 -3
  104. package/dist/esm/context/audit.js +5 -8
  105. package/dist/esm/context/index.d.ts +3 -4
  106. package/dist/esm/context/index.js +3 -4
  107. package/dist/esm/index.d.ts +19 -13
  108. package/dist/esm/index.js +18 -15
  109. package/dist/esm/kits/answering/tasks.d.ts +6 -6
  110. package/dist/esm/kits/answering/tasks.js +5 -5
  111. package/dist/esm/kits/domains/catalog.d.ts +42 -0
  112. package/dist/esm/kits/domains/catalog.js +110 -0
  113. package/dist/esm/kits/domains/index.d.ts +4 -0
  114. package/dist/esm/kits/domains/index.js +4 -0
  115. package/dist/esm/kits/domains/registry.d.ts +100 -0
  116. package/dist/esm/kits/{intents → domains}/registry.js +77 -31
  117. package/dist/esm/kits/{intents → domains}/scopes.d.ts +18 -18
  118. package/dist/esm/kits/{intents → domains}/scopes.js +21 -21
  119. package/dist/esm/kits/index.d.ts +6 -5
  120. package/dist/esm/kits/index.js +6 -5
  121. package/dist/esm/kits/presentation/formats.d.ts +42 -0
  122. package/dist/esm/kits/presentation/formats.js +105 -0
  123. package/dist/esm/kits/presentation/index.d.ts +14 -5
  124. package/dist/esm/kits/presentation/index.js +10 -5
  125. package/dist/esm/kits/reflection.js +3 -3
  126. package/dist/esm/kits/text-blocks.d.ts +24 -0
  127. package/dist/esm/kits/text-blocks.js +97 -0
  128. package/dist/esm/kits/tools.d.ts +69 -52
  129. package/dist/esm/kits/tools.js +246 -158
  130. package/dist/esm/model/index.d.ts +2 -6
  131. package/dist/esm/model/index.js +2 -6
  132. package/dist/esm/model/interactions/calls.d.ts +40 -0
  133. package/dist/esm/model/interactions/calls.js +58 -0
  134. package/dist/esm/model/interactions/json-object.js +11 -11
  135. package/dist/esm/model/interactions/native-functions.js +17 -23
  136. package/dist/esm/model/message-diagnostics.d.ts +6 -5
  137. package/dist/esm/model/message-diagnostics.js +13 -3
  138. package/dist/esm/model/options.js +2 -0
  139. package/dist/esm/model/preparation.js +39 -6
  140. package/dist/esm/model/runner.js +14 -0
  141. package/dist/esm/model/selection-transport.js +4 -4
  142. package/dist/esm/model/token-budget.js +3 -0
  143. package/dist/esm/model/transports.d.ts +2 -1
  144. package/dist/esm/model/transports.js +3 -2
  145. package/dist/esm/structs/capabilities/invocation.d.ts +1 -1
  146. package/dist/esm/structs/capabilities/model.d.ts +42 -11
  147. package/dist/esm/structs/capabilities/tools.d.ts +9 -4
  148. package/dist/esm/structs/common.d.ts +1 -1
  149. package/dist/esm/structs/index.d.ts +7 -6
  150. package/dist/esm/structs/index.js +7 -6
  151. package/dist/esm/structs/kits/answering/tasks.d.ts +2 -2
  152. package/dist/esm/structs/kits/{intents → domains}/catalog.d.ts +20 -20
  153. package/dist/esm/structs/kits/{intents → domains}/registry.d.ts +24 -21
  154. package/dist/esm/structs/kits/{intents → domains}/scopes.d.ts +9 -9
  155. package/dist/esm/structs/kits/presentation/formats.d.ts +42 -0
  156. package/dist/esm/structs/kits/presentation/formats.js +1 -0
  157. package/dist/esm/structs/kits/text-blocks.d.ts +25 -0
  158. package/dist/esm/structs/kits/text-blocks.js +10 -0
  159. package/dist/esm/structs/kits/tools.d.ts +82 -83
  160. package/dist/esm/structs/model/interactions.d.ts +18 -11
  161. package/dist/esm/structs/model/messages.d.ts +34 -2
  162. package/dist/esm/structs/model/runtime.d.ts +7 -0
  163. package/dist/esm/structs/model/session.d.ts +29 -0
  164. package/dist/esm/structs/model/session.js +10 -0
  165. package/dist/esm/structs/support/associations.d.ts +22 -0
  166. package/dist/esm/structs/support/associations.js +1 -0
  167. package/dist/esm/structs/support/model/interactions.d.ts +2 -2
  168. package/dist/esm/structs/support/model/messages.d.ts +6 -13
  169. package/dist/esm/structs/support/tools.d.ts +23 -23
  170. package/dist/esm/support/associations.d.ts +26 -0
  171. package/dist/esm/support/associations.js +76 -0
  172. package/dist/esm/support/context/snapshot.js +11 -1
  173. package/dist/esm/support/decorators/index.d.ts +13 -2
  174. package/dist/esm/support/decorators/index.js +40 -3
  175. package/dist/esm/support/decorators/pool.d.ts +6 -5
  176. package/dist/esm/support/decorators/types.d.ts +3 -1
  177. package/dist/esm/support/index.d.ts +1 -0
  178. package/dist/esm/support/index.js +1 -0
  179. package/dist/esm/support/model/capabilities.d.ts +8 -3
  180. package/dist/esm/support/model/capabilities.js +35 -5
  181. package/dist/esm/support/model/chat-completions.js +11 -2
  182. package/dist/esm/support/model/completion/calls.d.ts +6 -34
  183. package/dist/esm/support/model/completion/calls.js +8 -55
  184. package/dist/esm/support/model/index.d.ts +1 -1
  185. package/dist/esm/support/model/preview-rules.d.ts +13 -8
  186. package/dist/esm/support/model/preview-rules.js +11 -11
  187. package/dist/esm/support/model/reply-preview.d.ts +2 -2
  188. package/dist/esm/support/model/reply-preview.js +3 -3
  189. package/dist/esm/support/model/session.d.ts +61 -0
  190. package/dist/esm/support/model/session.js +148 -0
  191. package/dist/esm/support/model/stage-transport.d.ts +1 -1
  192. package/dist/esm/support/model/stage-transport.js +9 -9
  193. package/dist/esm/support/model/transports.d.ts +2 -0
  194. package/dist/esm/support/model/transports.js +2 -1
  195. package/dist/esm/support/tools.d.ts +11 -3
  196. package/dist/esm/support/tools.js +43 -18
  197. package/dist/esm/utils/catalog.d.ts +1 -1
  198. package/package.json +20 -6
  199. package/dist/cjs/execution/index.js +0 -30
  200. package/dist/cjs/kits/intents/catalog.js +0 -82
  201. package/dist/cjs/kits/intents/index.js +0 -35
  202. package/dist/esm/execution/index.d.ts +0 -11
  203. package/dist/esm/execution/index.js +0 -11
  204. package/dist/esm/kits/intents/catalog.d.ts +0 -39
  205. package/dist/esm/kits/intents/catalog.js +0 -79
  206. package/dist/esm/kits/intents/index.d.ts +0 -22
  207. package/dist/esm/kits/intents/index.js +0 -18
  208. package/dist/esm/kits/intents/registry.d.ts +0 -88
  209. /package/dist/cjs/structs/kits/{intents → domains}/catalog.js +0 -0
  210. /package/dist/cjs/structs/kits/{intents → domains}/registry.js +0 -0
  211. /package/dist/cjs/structs/kits/{intents → domains}/scopes.js +0 -0
  212. /package/dist/esm/structs/kits/{intents → domains}/catalog.js +0 -0
  213. /package/dist/esm/structs/kits/{intents → domains}/registry.js +0 -0
  214. /package/dist/esm/structs/kits/{intents → domains}/scopes.js +0 -0
@@ -42,7 +42,7 @@ catalog.bind({ app, execution }): ToolRuntime
42
42
  runtime.invoke({ toolId, input }): Promise<ToolResult<InferredOutput>>
43
43
  ```
44
44
 
45
- catalog.capabilities projects the same jsonSchema from the tool declarations; its capability adapter checks only the raw parameter object and the selection contract, resolve returns `{ toolId, input }`, and input keeps the raw parameters. It does not run the tool's business inputSchema.parse early; the tool parses once in the real invoke before authorization and execution, avoiding repeated runs of transforming parsers. A custom model stage may register its own parsing and resolve, but a converted value must not be passed off again as the tool's raw input.
45
+ catalog.tools projects the same jsonSchema from the tool declarations; its capability adapter checks only the raw parameter object and the selection contract, resolve returns `{ toolId, input }`, and input keeps the raw parameters. It does not run the tool's business inputSchema.parse early; the tool parses once in the real invoke before authorization and execution, avoiding repeated runs of transforming parsers. A custom model stage may register its own parsing and resolve, but a converted value must not be passed off again as the tool's raw input.
46
46
 
47
47
  Catalog implements the shared read-only collection interface. After a static selection, the inferred tool IDs and result types narrow together; dynamic IDs go through the unknown boundary. Context/App do not enter the model schema and cannot be overridden by model parameters.
48
48
 
@@ -69,8 +69,8 @@ The composition layer only verifies references and assembly and does not underst
69
69
  ```ts
70
70
  Support.createTextTransport({ id, options? }): ModelTransportDefinition<string>
71
71
  Support.createJsonTransport({ id, outputSchema, options? }): ModelTransportDefinition<Output>
72
- Support.createModelTransport({ id, capabilities, adapter?, options?, requireStrictArguments? }): ModelTransportDefinition
73
- Support.createStageModelTransport({ id, capabilities, stages, adapter?, options? }): ModelTransportDefinition
72
+ Support.createModelTransport({ id, tools, adapter?, options?, requireStrictArguments? }): ModelTransportDefinition
73
+ Support.createStageModelTransport({ id, tools, stages, adapter?, options? }): ModelTransportDefinition
74
74
  ```
75
75
 
76
76
  `createJsonTransport` is used to generate JSON values that conform to a Schema directly, such as summaries, classification results, or tabular data. `outputSchema` is an ordinary `SchemaAdapter<Output>` containing `jsonSchema` and a synchronous `parse(value)`; it can be provided directly, built with a Support Schema factory, or passed as a stateful class implementation. The parser is called after JSON decoding; declaring a JSON Schema does not replace runtime validation.
@@ -90,9 +90,9 @@ console.log(result.value.text, result.invocation);
90
90
 
91
91
  The ID keeps its literal type; after registration the original transport handle or the ID can be used for calls. Each call creates a new collector, and parts accumulate only within that call. Invalid JSON, Schema parser rejection, asynchronous parsers, and function-call parts all propagate as failures and produce no deliverable success value. Input/output/Schema budgets, cancellation, and call limits continue to be enforced by the same Runner.
92
92
 
93
- It requires no capability list and introduces no capability/input envelope; when a model must choose among registered capabilities, use `createModelTransport` or `createStageModelTransport`. The JSON request mode and the JSON value types a provider actually supports are decided by the Provider. Output remains uniformly `{ value, invocation }` and does not become a bare object in JSON mode. `options` accepts only temperature overrides and does not relax Provider and Runner constraints.
93
+ It requires no capability list and introduces no capability/input envelope; when a model must choose among registered tools, use `createModelTransport` or `createStageModelTransport`. The JSON request mode and the JSON value types a provider actually supports are decided by the Provider. Output remains uniformly `{ value, invocation }` and does not become a bare object in JSON mode. `options` accepts only temperature overrides and does not relax Provider and Runner constraints.
94
94
 
95
- capabilities is a ModelCapabilitySource or a source factory read by stageId. A stages item uses `{ id, capabilityIds }`. Static sources validate all references at assembly, and dynamic sources refresh when the interaction is prepared for this call. An unknown stage does not invoke a dynamic source and does not extend to other stages.
95
+ tools is a ModelToolSource or a source factory read by stageId. A stages item uses `{ id, toolIds }`. Static sources validate all references at assembly, and dynamic sources refresh when the interaction is prepared for this call. An unknown stage does not invoke a dynamic source and does not extend to other stages.
96
96
 
97
97
  prepare(stageId) returns the unified collector protocol; a prepared interaction fixes the resolver and receiver for this call, and the next dynamic preparation can obtain a new source. Native and JSON adapters consume the same capability projection. The native adapter validates the object shape, index, ID, name, and argument strings of neutral function-call-delta values; invalid parts do not enter capability execution. The collector's outputChars counting and call metadata reading boundaries follow the [Core transport contract](core.md). Fallback is chosen explicitly by the App and is not guessed implicitly from model names or URLs.
98
98
 
@@ -102,7 +102,7 @@ prepare(stageId) returns the unified collector protocol; a prepared interaction
102
102
  Support.createModelStrategy({ model, models?, reasoning?, reasoningOptions?, budget?, ...coreSettings }): ModelRunnerOptions
103
103
  ```
104
104
 
105
- `model` accepts a model definition or an ID in `models`. `reasoning` selects a configuration explicitly registered in `reasoningOptions`; for example, high/off correspond to a Provider already configured with provider reasoning parameters. Unknown IDs or reasoning schemes raise errors, and model capabilities are not inferred from names.
105
+ `model` accepts a model definition or an ID in `models`. `reasoning` selects a configuration explicitly registered in `reasoningOptions`; for example, high/off correspond to a Provider already configured with provider reasoning parameters. Unknown IDs or reasoning schemes raise errors, and model tools are not inferred from names.
106
106
 
107
107
  The budget defaults to medium. These are overridable local task budgets, not guarantees of provider capacity or performance:
108
108
 
@@ -161,7 +161,7 @@ If no budget is configured or passed with the request, construction raises an er
161
161
 
162
162
  modelInteractionCatalog selects a native function or JSON adapter by an explicitly registered interaction ID; the App supplies selection metadata, can override explicitly, and does not infer capability from brand, model name, or URL.
163
163
 
164
- Preview rules use stageId and capabilityId; previews are transient and cannot authorize, persist, or execute model proposals. ModelOutputEvent goes to the sink and ProviderObservation goes to the observer; they are not mixed.
164
+ Preview rules use stageId and toolId; previews are transient and cannot authorize, persist, or execute model proposals. ModelOutputEvent goes to the sink and ProviderObservation goes to the observer; they are not mixed.
165
165
 
166
166
  External SSE/wire types can keep standard fields and are converted at the Support boundary; the protocol itself is not changed to standardize SDK spelling.
167
167
 
@@ -171,7 +171,7 @@ Completion determination is selected through `ports.completionPolicy` and defaul
171
171
 
172
172
  `Support.createToolCallCollector` aggregates call parts by index, and the completion policy and direct callers share the same implementation. Conflicting part IDs, duplicate IDs across indices, and incomplete calls are rejected as protocol errors, and one complete call does not mask another incomplete call.
173
173
 
174
- The protocol layer never restricts receiving multiple call parts, and `parallel_tool_calls` is only a send field that expresses neither execution authorization nor scheduling policy. The single-proposal limit on the Agent path is decided by the `nativeFunctionInteraction` collector; using the Provider directly is not subject to that limit and does not thereby gain tool execution, authorization, or loop capabilities.
174
+ The protocol layer never restricts receiving multiple call parts, and `parallel_tool_calls` is only a send field that expresses neither execution authorization nor scheduling policy. The single-proposal limit on the Agent path is decided by the `nativeFunctionInteraction` collector; using the Provider directly is not subject to that limit and does not thereby gain tool execution, authorization, or loop tools.
175
175
 
176
176
  `ProviderMessage` provides the `tool` result and assistant `toolCalls` shapes needed for direct connection, and `chatCompletionMessages` maps them explicitly to `tool_call_id` and nested `tool_calls`; mismatched roles or fields and incomplete call fields are rejected as `TypeError`. Only declared fields are sent, and extra fields (including raw wire fields) are not passed through; `toolCallId` must be a non-empty string, and `role` and `content` are validated per the message contract. The Core `ModelMessage` and `ChatProvider` abstract signatures are unchanged.
177
177
 
@@ -222,7 +222,7 @@ request.callModel({ stageId, input, maxOutputTokens, transport }): Promise<Model
222
222
 
223
223
  request provides stageId and the fragments to compress or candidates to select; the result parser handles only the returned value. Policies reference handlers by handlerId; callModel takes a named request and returns a ModelResult. The cancellation signal is read from the second context. Processing nodes connect these callbacks through the Workflow model port; for original-text protection, history grouping, watermarks, reserved calls, and audit rules, see [Context nodes](context-processing.md).
224
224
 
225
- createReplyPreview uses `{ rules, sink, render?, maxChars? }`; rules use stageId and capabilityId (native adaptation maps the latter to the function wire name). The sink is awaited in order, and render is synchronous. Rule data is copied as a snapshot and callbacks fix the original receiver; a preview never authorizes or executes tools.
225
+ createReplyPreview uses `{ rules, sink, render?, maxChars? }`; rules use stageId and toolId (native adaptation maps the latter to the function wire name). The sink is awaited in order, and render is synchronous. Rule data is copied as a snapshot and callbacks fix the original receiver; a preview never authorizes or executes tools.
226
226
 
227
227
  When a selector omits `instruction`, it uses the built-in trimming selection instruction, which requires the model to return `{ stepIds }` and explicitly treats fragment excerpts as data. A custom parser must provide the corresponding instruction as well.
228
228
 
@@ -250,3 +250,49 @@ const settings = Support.createModelStrategy({ model: { provider }, maxOutputTok
250
250
  ```
251
251
 
252
252
  JSON value transports enable `json` mode by default. This mode in CompatibleModelProvider uses `json_object`; when an array or scalar is needed and the provider does not support the corresponding mode, set `json: false` explicitly in the same `options` while keeping full JSON decoding and outputSchema validation.
253
+
254
+ ## Model Tools and demand discovery
255
+
256
+ `Support.defineModelTools(definitions)` compiles BaseTool declarations. Each has id, description, inputSchema and a synchronous resolve. When resolve is omitted, Support uses the parsed input as the result. ExecutableTool adds authorization, execution and output validation; `defineToolCatalog` derives its model view as `catalog.tools` without requiring a separate capability declaration.
257
+
258
+ ```ts
259
+ const tools = Support.defineModelTools([{
260
+ id: 'manage_memo',
261
+ description: 'Process a memo draft',
262
+ inputSchema: memoSchema,
263
+ asIntent: {
264
+ description: 'Remember something or set a reminder',
265
+ when: context => context.features.memos,
266
+ delegates: ['read', 'final'],
267
+ },
268
+ active: context => context.services.memos != null,
269
+ resolve: input => input,
270
+ }]);
271
+ const discovery = tools.withIntent(context);
272
+ const explicit = tools.select(['manage_memo'], context);
273
+ ```
274
+
275
+ `asIntent` accepts true, a nonblank description, a synchronous boolean predicate, or `{ description?, when?, delegates? }`. Omission means not participating in demand discovery; no static false is needed. Missing description reuses the Tool description. Missing when enables discovery. Delegates are an optional allowed set, validated against domain vocabulary when using a domain registry, not an execution order. Intent has no independent identity or registry. Similar descriptions on different tools remain separate.
276
+
277
+ `active` accepts a boolean or synchronous predicate and defaults to true. Selection filters activation; prepared projections and execution recheck it before accepting or performing work. Activation does not replace authorization. Node code owns candidate selection and can use withIntent at any stage; Engine does not automatically publish tools based on this metadata.
278
+
279
+ ## Delegates and dispatch
280
+
281
+ A delegate is one optional tool field: `delegate: 'query'` claims that id. The tool registry keeps a unique `delegateId → tool` index, so a node only calls `registry.call(next, context)` and the registry resolves `next.type` to its implementation.
282
+
283
+ A delegate creates no second Tool identity and no second execution path: the matched tool still runs the same `execute`, authorization and lifecycle. The allowed delegate set stays in the domain declaration (for example a GoalTool's `delegates`); it lists the next steps a model may pick and is not an execution order.
284
+
285
+ ## Associated Tool registration
286
+
287
+ ```ts
288
+ const linked = Support.defineAssociatedCatalog(owners, {
289
+ tools: independentTools,
290
+ toolDefaults: owner => existingTools.require(owner.id),
291
+ });
292
+ // owner.modelTools: an array of inline Tool overrides or independent Tool IDs.
293
+ // linked.tools: normalized model Tool catalog.
294
+ // linked.bindings: owner ID -> toolIds.
295
+ // linked.sources: local provenance; not automatically sent to the model.
296
+ ```
297
+
298
+ References name explicitly declared independent tools, never a tool accidentally contributed by another owner. Duplicate identities, repeated references and missing contracts are rejected. An empty modelTools array suppresses default projection. Tool descriptions are not aggregated into Intent entities. Decorator conversion does not accept parallel asIntent configuration: combine WorkflowTool and a separate Intent decorator on the same target. See [the complete runnable comparison](configuration-in-workflow.en.md).
@@ -69,8 +69,8 @@ Support.defineRegistrationCatalog(definitions): RegistrationCatalog
69
69
  ```ts
70
70
  Support.createTextTransport({ id, options? }): ModelTransportDefinition<string>
71
71
  Support.createJsonTransport({ id, outputSchema, options? }): ModelTransportDefinition<Output>
72
- Support.createModelTransport({ id, capabilities, adapter?, options?, requireStrictArguments? }): ModelTransportDefinition
73
- Support.createStageModelTransport({ id, capabilities, stages, adapter?, options? }): ModelTransportDefinition
72
+ Support.createModelTransport({ id, tools, adapter?, options?, requireStrictArguments? }): ModelTransportDefinition
73
+ Support.createStageModelTransport({ id, tools, stages, adapter?, options? }): ModelTransportDefinition
74
74
  ```
75
75
 
76
76
  `createJsonTransport` 用于直接生成符合 Schema 的 JSON 值,例如摘要、分类结果或表格数据。`outputSchema` 是普通 `SchemaAdapter<Output>`,包含 `jsonSchema` 和同步 `parse(value)`;可直接提供、使用 Support Schema 工厂,或传入有状态的类实现。JSON 解码后再调用该 parser;声明 JSON Schema 不替代运行时校验。
@@ -92,7 +92,7 @@ ID 保留字面量类型;登记后可用原 transport 句柄或 ID 调用。
92
92
 
93
93
  它不要求能力清单,也不引入 capability/input envelope;需要模型从已注册能力中作选择时,使用 `createModelTransport` 或 `createStageModelTransport`。JSON 请求方式及供应商实际支持的 JSON 值类型由 Provider 决定。输出仍统一为 `{ value, invocation }`,不会因 JSON 模式变成裸对象。`options` 只接受温度覆盖,不放宽 Provider 和 Runner 的约束。
94
94
 
95
- capabilities是ModelCapabilitySource或按stageId读取的source工厂。stages项使用`{ id, capabilityIds }`。静态source装配时校验全部引用,动态source在本次准备交互时刷新。未知阶段不调用动态source,不扩大到其他阶段。
95
+ capabilities是ModelToolSource或按stageId读取的source工厂。stages项使用`{ id, toolIds }`。静态source装配时校验全部引用,动态source在本次准备交互时刷新。未知阶段不调用动态source,不扩大到其他阶段。
96
96
 
97
97
  prepare(stageId)返回统一的collector协议;准备好的交互固定本次resolver及接收者;下一次动态准备可取得新source。native与JSON adapter消费同一能力投影。原生 adapter 校验中立 function-call-delta 的对象形状、index、ID、名称和参数字符串;非法分片不进入能力执行。collector 的 outputChars 计数和 call 元数据读取边界遵守 [Core 传输合同](core.md)。回退由App显式选择,不按模型名或URL隐式猜测。
98
98
 
@@ -161,7 +161,7 @@ Support 的策略模式是 `validate | reduce | audit`。`createContextProcessin
161
161
 
162
162
  modelInteractionCatalog 按明确登记的交互 ID 选择原生函数或 JSON adapter;App 提供选择元数据,可明确覆盖,不按品牌、模型名或 URL 推断能力。
163
163
 
164
- 预览规则使用stageId与capabilityId;预览是暂态,不能授权、持久化或执行模型提议。ModelOutputEvent进sink,ProviderObservation进observer,不混用。
164
+ 预览规则使用stageId与toolId;预览是暂态,不能授权、持久化或执行模型提议。ModelOutputEvent进sink,ProviderObservation进observer,不混用。
165
165
 
166
166
  外部SSE/wire类型可以保留标准字段,并在Support边界转换;不为统一SDK拼写修改协议本身。
167
167
 
@@ -171,9 +171,9 @@ modelInteractionCatalog 按明确登记的交互 ID 选择原生函数或 JSON a
171
171
 
172
172
  `Support.createToolCallCollector` 按 index 聚合调用分片,完成策略与直接调用方共用同一实现。分片 ID 冲突、跨 index 重复 ID 与残缺调用都按协议错误拒绝,一个完整调用不会掩盖另一个残缺调用。
173
173
 
174
- 协议层从不限制接收多个调用分片,`parallel_tool_calls` 只是发送字段,不表达执行授权或调度策略。Agent 路径的单提案限制由 `nativeFunctionInteraction` 的 collector 决定;直接使用 Provider 不受该限制,也不因此获得工具执行、授权或循环能力。
174
+ 协议层从不限制接收多个调用分片,`parallel_tool_calls` 只是发送字段,不表达执行授权或调度策略。Agent 路径的单提案限制由 `nativeFunctionInteraction` 的 collector 决定;直接使用 Provider 不受该限制,也不因此获得工具执行、授权或循环能力。需要正文 + 多调用时使用 `Support.createSessionModelTransport`,它复用同一聚合 collector,但不改变 `nativeFunctionInteraction` 的单提案合同。`createSessionModelTransport` 返回 `ResolvedSessionResult`,每个调用的 `toolId`、解析后的 `input` 与非致命 `problem` 都由 Support 解析;Core 只提供原始 `ModelSessionResult`。
175
175
 
176
- `ProviderMessage` 提供直连所需的 `tool` 结果与 assistant `toolCalls` 形状,`chatCompletionMessages` 显式映射为 `tool_call_id` 与嵌套 `tool_calls`;角色与字段不匹配、调用字段不完整时按 `TypeError` 拒绝。只发送声明字段,额外字段(包括原始 wire 字段)不透传;`toolCallId` 必须是非空字符串,`role` 与 `content` 按消息合同校验。Core `ModelMessage` 与 `ChatProvider` 抽象签名不变。
176
+ `ProviderMessage` 提供直连所需的 `tool` 结果与 assistant `toolCalls` 形状,`chatCompletionMessages` 显式映射为 `tool_call_id` 与嵌套 `tool_calls`;角色与字段不匹配、调用字段不完整时按 `TypeError` 拒绝。只发送声明字段,额外字段(包括原始 wire 字段)不透传;`toolCallId` 必须是非空字符串,`role` 与 `content` 按消息合同校验。Core `ModelMessage` 现已支持 assistant `toolCalls` 与 `role:'tool'` 字段;`ChatProvider` 抽象签名仍为 `readonly ModelMessage[]`。
177
177
 
178
178
  直连工具线程使用声明字段,assistant 的 `content` 可以为空字符串;工具结果通过同一个调用 ID 关联:
179
179
 
@@ -222,7 +222,7 @@ request.callModel({ stageId, input, maxOutputTokens, transport }): Promise<Model
222
222
 
223
223
  request 提供 stageId 及压缩片段/选择候选;结果 parser 只处理返回的 value。策略引用使用 handlerId;callModel 接收命名请求并返回 ModelResult。取消信号从第二个 context 读取。处理节点通过 Workflow 模型端口接入这些回调,保护原文、历史分组、水位、预留调用和审计规则见[上下文节点](context-processing.md)。
224
224
 
225
- createReplyPreview使用`{ rules, sink, render?, maxChars? }`;规则使用stageId和capabilityId(native适配将其映射为函数wire name)。sink按序等待,render同步。规则数据快照复制,回调固定原接收者;预览永不授权或执行工具。
225
+ createReplyPreview使用`{ rules, sink, render?, maxChars? }`;规则使用stageId和toolId(native适配将其映射为函数wire name)。sink按序等待,render同步。规则数据快照复制,回调固定原接收者;预览永不授权或执行工具。
226
226
 
227
227
  选择器省略 `instruction` 时使用内置的裁切选择指令,要求模型返回 `{ stepIds }`,并明确将片段摘录视为数据。自定义解析器须同时提供对应指令。
228
228
 
@@ -250,3 +250,49 @@ const settings = Support.createModelStrategy({ model: { provider }, maxOutputTok
250
250
  ```
251
251
 
252
252
  JSON 值传输缺省启用 `json` 模式。CompatibleModelProvider 的该模式使用 `json_object`;需要数组/标量而供应商不支持对应模式时,可在同一 `options` 中显式设 `json: false`,仍保留完整 JSON 解码和 outputSchema 校验。
253
+
254
+ ## 模型 Tool、用途与激活
255
+
256
+ `Support.defineModelTools(definitions)` 编译 BaseTool 声明:id、description、inputSchema 和同步 resolve。省略 resolve 时,Support 将已解析输入作为结果。ExecutableTool 在 BaseTool 上增加授权、执行与结果校验;`defineToolCatalog` 从同一声明生成 `catalog.tools` 模型视图,无需另配 Capability。
257
+
258
+ ```ts
259
+ const tools = Support.defineModelTools([{
260
+ id: 'manage_memo',
261
+ description: 'Process a memo draft',
262
+ inputSchema: memoSchema,
263
+ asIntent: {
264
+ description: 'Remember something or set a reminder',
265
+ when: context => context.features.memos,
266
+ delegates: ['read', 'final'],
267
+ },
268
+ active: context => context.services.memos != null,
269
+ resolve: input => input,
270
+ }]);
271
+ const discovery = tools.withIntent(context);
272
+ const explicit = tools.select(['manage_memo'], context);
273
+ ```
274
+
275
+ `asIntent` 接受 true、非空描述、同步布尔判断函数或 `{ description?, when?, delegates? }`。省略表示不参与需求识别,不提供静态 false。描述省略时复用 Tool 描述,when 省略时参与。delegates 可选,表示允许集合,由领域注册器校验词汇,不表示执行顺序。Intent 没有独立身份或注册表;不同 Tool 的相似描述分别保留。
276
+
277
+ `active` 接受布尔值或同步判断函数,省略默认激活。获取时筛选,已准备视图与执行入口在处理时复核;激活不能代替鉴权。节点自主组织候选,任何阶段都可以使用 withIntent;Engine 不依据标记自动投放工具。
278
+
279
+ ## delegate 与分派
280
+
281
+ delegate 是工具上的一个可选字段:`delegate: 'query'` 表示该工具认领这个 id。工具注册表维护 `delegateId → tool` 索引并在注册时保证唯一,节点分发时只需 `registry.call(next, context)`,由注册表按 `next.type` 找到实现。
282
+
283
+ delegate 不创建第二个 Tool 身份,也不改变执行入口:命中后仍走同一个 `execute`、鉴权与生命周期。delegates 的允许集合仍由领域声明(例如 GoalTool 的 `delegates`)提供,只表示模型可选的下一步,不表示执行顺序。
284
+
285
+ ## Tool 关联注册
286
+
287
+ ```ts
288
+ const linked = Support.defineAssociatedCatalog(owners, {
289
+ tools: independentTools,
290
+ toolDefaults: owner => existingTools.require(owner.id),
291
+ });
292
+ // owner.modelTools: an array of inline Tool overrides or independent Tool IDs.
293
+ // linked.tools: normalized model Tool catalog.
294
+ // linked.bindings: owner ID -> toolIds.
295
+ // linked.sources: local provenance; not automatically sent to the model.
296
+ ```
297
+
298
+ 引用只指向显式独立 Tool,不隐式依赖另一个 owner 恰好贡献的工具。重名、重复引用和缺失合同拒绝;modelTools 空数组关闭该 owner 的缺省投影。不会把工具描述聚合为 Intent 实体。装饰器转换不接收并联 asIntent:同一目标分别使用 WorkflowTool 和 Intent 装饰器。完整代码见[独立配置与关联配置](configuration-in-workflow.md)。
@@ -1,5 +1,39 @@
1
1
  # Shared types and behavior contracts
2
2
 
3
+ ## Common Tool contract and extensions
4
+
5
+ BaseTool is the common model-facing interface. Subtypes add actual contracts rather than names for different uses.
6
+
7
+ ```ts
8
+ interface BaseTool<Input = unknown, Selection = Input, Context = any> {
9
+ readonly id: string;
10
+ readonly description: string;
11
+ readonly inputSchema: SchemaAdapter<Input>;
12
+ readonly active?: boolean | ((context: Readonly<Context>) => boolean);
13
+ readonly asIntent?: true | string
14
+ | ((context: Readonly<Context>) => boolean)
15
+ | {
16
+ readonly description?: string;
17
+ readonly when?: (context: Readonly<Context>) => boolean;
18
+ readonly delegates?: readonly string[];
19
+ };
20
+ resolve(input: Input, context: Readonly<{ stageId: string }>): Selection;
21
+ }
22
+
23
+ interface ExecutableTool<Input, Output, App>
24
+ extends BaseTool<Input, ToolProposal, App> {
25
+ readonly kind: string;
26
+ authorize(input: Input, context: OperationContext<App>): MaybePromise<void>;
27
+ execute(input: Input, context: OperationContext<App> & NodeReportingContext): MaybePromise<Output>;
28
+ parseOutput(value: unknown): Output;
29
+ debugSteps?(result: Output, context: NodeReportingContext): void;
30
+ }
31
+ ```
32
+
33
+ ExecutableTool adds execution, Tool adds one optional lifecycle block for invocation and delivery hooks, IntentTool adds optional domain policies, and Support.PreviewTool adds streaming presentation. Lifecycle execution uses a different context from typed execution, so both extend the common ModelTool rather than forcing identical handlers. Cafe's GoalTool narrows domain policies in the App.
34
+
35
+ There is no independent Intent identity. asIntent marks discovery use and optionally supplies a demand-facing description, condition and allowed delegates. active conditions remain distinct from authorization. Normalized tools always have resolve; Support fills identity mappings or executable Tool proposals when omitted in configuration. Neither mapping executes a business operation. See [Support](support.en.md) and [Public Symbol Reference](reference.en.md) for the full registration and subtype signatures.
36
+
3
37
  [Back to API handbook](README.md). This page describes the base types and invocation rules shared by all modules.
4
38
 
5
39
  ## Identity, configuration, and time
@@ -47,7 +81,7 @@ interface OperationContext<App> {
47
81
  Core supplies the actual signal when it creates a request instance; the signal received by a registered handler always exists. `app` is the business dependency supplied by the caller, and models cannot override it. A Kit may define a local execution context that extends this contract, but it cannot require other Kits to fill its fields.
48
82
 
49
83
  - A request instance binds execution at construction, so later invoke calls do not need to pass signal again.
50
- - Reusable capabilities receive context on every run/read/resolve.
84
+ - Reusable tools receive context on every run/read/resolve.
51
85
  - App service identity, budget, and cancellation travel along the real call chain; nesting a Kit never grants new budget.
52
86
 
53
87
  ## Schema and parsing
@@ -2,6 +2,49 @@
2
2
 
3
3
  [返回 API 手册](README.md)。本页说明各模块共用的基础类型与调用规则。
4
4
 
5
+ ## Tool 的共同接口与扩展
6
+
7
+ 模型可选择的接口统一使用 BaseTool;只有确实增加合同的类型才继承,不为每种用途建立同义子类。
8
+
9
+ ```ts
10
+ interface BaseTool<Input = unknown, Selection = Input, Context = any> {
11
+ readonly id: string;
12
+ readonly description: string;
13
+ readonly inputSchema: SchemaAdapter<Input>;
14
+ readonly active?: boolean | ((context: Readonly<Context>) => boolean);
15
+ readonly asIntent?: true | string
16
+ | ((context: Readonly<Context>) => boolean)
17
+ | {
18
+ readonly description?: string;
19
+ readonly when?: (context: Readonly<Context>) => boolean;
20
+ readonly delegates?: readonly string[];
21
+ };
22
+ resolve(input: Input, context: Readonly<{ stageId: string }>): Selection;
23
+ }
24
+
25
+ interface ExecutableTool<Input, Output, App>
26
+ extends BaseTool<Input, ToolProposal, App> {
27
+ readonly kind: string;
28
+ authorize(input: Input, context: OperationContext<App>): MaybePromise<void>;
29
+ execute(input: Input, context: OperationContext<App> & NodeReportingContext): MaybePromise<Output>;
30
+ parseOutput(value: unknown): Output;
31
+ debugSteps?(result: Output, context: NodeReportingContext): void;
32
+ }
33
+ ```
34
+
35
+ ```text
36
+ ModelTool
37
+ ├─ ExecutableTool:授权、执行、输出校验
38
+ ├─ Tool:一个可选 lifecycle 块,覆盖调用、上下文与交付
39
+ ├─ IntentTool:领域策略与允许 delegate
40
+ │ └─ GoalTool:Cafe 的具体枚举及约束
41
+ └─ Support.PreviewTool:模型输出的流式预览
42
+ ```
43
+
44
+ Tool 与 ExecutableTool 的执行上下文不同,分别继承共同模型合同,保留各自真实执行边界。IntentTool 使用相同 Tool 身份、Schema 与 resolver,不是独立 Intent 实体;delegates 可以从关联推导或由 asIntent.delegates 覆盖。GoalTool 属于应用,不成为 Core 的必填配置。
45
+
46
+ resolve 在规范化合同中必定存在;Support 配置入口允许省略恒等映射。执行 Tool 的默认映射产生 `{ toolId, input }`,不是直接执行。active 与 asIntent 的回调只在本地求值;Schema、名称及投影后的描述才提交给模型。完整子类字段和注册形式见[公开符号参考](reference.md),选取方法见[Support](support.md)。
47
+
5
48
  ## 身份、配置和时间
6
49
 
7
50
  ```ts
@@ -45,7 +45,7 @@ const double = Support.defineTool({
45
45
  },
46
46
  });
47
47
  const catalog = Support.defineToolCatalog([double]);
48
- const selection = Support.createModelTransport({ id: 'choose-tool', capabilities: catalog.capabilities });
48
+ const selection = Support.createModelTransport({ id: 'choose-tool', tools: catalog.tools });
49
49
  ```
50
50
 
51
51
  inputSchema contains both the model-visible jsonSchema and the real parser. The selection stage keeps only the raw proposed parameters; when the tool executes they are parsed once, so a converting parser does not run twice. authorize may only return void or throw; asynchronous authorization uses the asynchronous run entry. Permissions come from server-side context, not from model parameters.
@@ -45,7 +45,7 @@ const double = Support.defineTool({
45
45
  },
46
46
  });
47
47
  const catalog = Support.defineToolCatalog([double]);
48
- const selection = Support.createModelTransport({ id: 'choose-tool', capabilities: catalog.capabilities });
48
+ const selection = Support.createModelTransport({ id: 'choose-tool', tools: catalog.tools });
49
49
  ```
50
50
 
51
51
  inputSchema同时包含模型可见jsonSchema和真实parser。选择阶段仅保留原始提议参数;工具执行时解析一次,转换型parser不会被运行两遍。authorize只能返回void或抛错,异步授权使用异步运行入口;权限来自服务端上下文,不从模型参数取得。
@@ -73,4 +73,4 @@ node SDK_PATH/development/skills/agent-core-app/scripts/create-app.mjs ./my-agen
73
73
 
74
74
  This mode compares the current digests of managed files, the generation baseline, and the template digests of the selected SDK, and reports modified, missing, symlink, and changes to the template itself. It checks only the code/configuration paths defined by the template, does not read `.env` or candidate knowledge body text, and does not modify the project. Keep the generation receipt as the basis for upgrades; when a new template must be adopted, generate in another directory and review the merge, and existing user code is never overwritten.
75
75
 
76
- Advanced capabilities use the existing Core/Support/Kit configuration: extend the same manifests, handlers, and execution entries to integrate subtasks, knowledge answering, or persistent tasks. Whether to record, how to store, and when to publish and restore are still implemented according to the application's actual product authorization.
76
+ Advanced tools use the existing Core/Support/Kit configuration: extend the same manifests, handlers, and execution entries to integrate subtasks, knowledge answering, or persistent tasks. Whether to record, how to store, and when to publish and restore are still implemented according to the application's actual product authorization.
@@ -0,0 +1,8 @@
1
+ /** One executable Tool declaration supplies both model and execution contracts. */
2
+ import { Support } from '@jangts/agent-core';
3
+ import { toolDefinitions } from './tools.mjs';
4
+
5
+ export function associatedConfiguration() {
6
+ const execution = Support.defineToolCatalog(toolDefinitions());
7
+ return { execution, tools: execution.tools };
8
+ }
@@ -0,0 +1,20 @@
1
+ /** Independent model and execution declarations, connected through checked Tool IDs. */
2
+ import { Support } from '@jangts/agent-core';
3
+ import { toolDefinitions, textInput } from './tools.mjs';
4
+
5
+ export function independentConfiguration() {
6
+ const execution = Support.defineToolCatalog(toolDefinitions());
7
+ const tools = Support.defineModelTools([
8
+ {
9
+ id: 'search_notes', description: '按主题检索公开笔记', asIntent: '用户希望查找笔记',
10
+ inputSchema: { jsonSchema: textInput('query').jsonSchema, parse: value => value },
11
+ resolve: input => ({ toolId: 'search_notes', input }),
12
+ },
13
+ {
14
+ id: 'read_note', description: '读取已定位笔记的正文',
15
+ inputSchema: { jsonSchema: textInput('noteId').jsonSchema, parse: value => value },
16
+ resolve: input => ({ toolId: 'read_note', input }),
17
+ },
18
+ ]);
19
+ return { execution, tools };
20
+ }
@@ -0,0 +1,48 @@
1
+ /** A name/input envelope changes model output format while preserving the same tool proposal. */
2
+ import { Support } from '@jangts/agent-core';
3
+
4
+ export function wrappedConfiguration(configuration) {
5
+ const capability = {
6
+ id: 'tool_request',
7
+ description: '提交工具名称和参数,由程序执行',
8
+ inputSchema: Support.defineSchema({
9
+ jsonSchema: {
10
+ type: 'object',
11
+ required: ['name', 'input'],
12
+ additionalProperties: false,
13
+ properties: {
14
+ name: { type: 'string', enum: configuration.tools.ids() },
15
+ input: { type: 'object' },
16
+ },
17
+ anyOf: configuration.tools.definitions.map((tool) => ({
18
+ properties: {
19
+ name: { const: tool.id },
20
+ input: tool.inputSchema.jsonSchema,
21
+ },
22
+ })),
23
+ },
24
+ parse(value) {
25
+ if (
26
+ !value ||
27
+ typeof value !== 'object' ||
28
+ Array.isArray(value) ||
29
+ Object.keys(value).some((key) => key !== 'name' && key !== 'input') ||
30
+ typeof value.name !== 'string' ||
31
+ !value.input ||
32
+ typeof value.input !== 'object' ||
33
+ Array.isArray(value.input)
34
+ ) {
35
+ throw new TypeError('Expected { name, input }');
36
+ }
37
+ configuration.tools.require(value.name);
38
+ return value;
39
+ },
40
+ }),
41
+ resolve: ({ name, input }) => ({ toolId: name, input }),
42
+ };
43
+ return {
44
+ ...configuration,
45
+ tools: Support.defineModelTools([capability]),
46
+ candidateIds: ['tool_request'],
47
+ };
48
+ }
@@ -0,0 +1,47 @@
1
+ /** Run the tutorial offline and check that both configuration styles produce equivalent contracts. */
2
+ import assert from 'node:assert/strict';
3
+ import { independentConfiguration } from './independent.mjs';
4
+ import { associatedConfiguration } from './associated.mjs';
5
+ import { runCase, taskTransport } from './workflow.mjs';
6
+ import { wrappedConfiguration } from './protocol.mjs';
7
+
8
+ const separate = independentConfiguration();
9
+ const linked = associatedConfiguration();
10
+ assert.deepEqual(linked.tools.withIntent({}).ids(), separate.tools.withIntent({}).ids());
11
+ const rawFunctions = (configuration) =>
12
+ taskTransport(configuration.tools).prepare('choose-operation').options.functions;
13
+ assert.deepEqual(rawFunctions(linked), rawFunctions(separate));
14
+ assert.deepEqual(
15
+ linked.tools.resolve(
16
+ { toolId: 'search_notes', input: { query: ' 咖啡 ' } },
17
+ { stageId: 'choose-operation' },
18
+ ),
19
+ separate.tools.resolve(
20
+ { toolId: 'search_notes', input: { query: ' 咖啡 ' } },
21
+ { stageId: 'choose-operation' },
22
+ ),
23
+ );
24
+ for (const [question, name, input] of [
25
+ ['帮我找咖啡相关的笔记', 'search_notes', { query: '咖啡' }],
26
+ ['读一下n1这篇笔记', 'read_note', { noteId: 'n1' }],
27
+ ]) {
28
+ const a = await runCase(separate, question, name, input);
29
+ const b = await runCase(linked, question, name, input);
30
+ assert.deepEqual(a.submitted, b.submitted);
31
+ assert.deepEqual(a.result, b.result);
32
+ console.log(JSON.stringify({ question, result: b.result }));
33
+ }
34
+ const a = await runCase(wrappedConfiguration(separate), '帮我找咖啡相关的笔记', 'tool_request', {
35
+ name: 'search_notes',
36
+ input: { query: '咖啡' },
37
+ });
38
+ const b = await runCase(wrappedConfiguration(linked), '帮我找咖啡相关的笔记', 'tool_request', {
39
+ name: 'search_notes',
40
+ input: { query: '咖啡' },
41
+ });
42
+ assert.deepEqual(a.submitted, b.submitted);
43
+ assert.deepEqual(a.result, b.result);
44
+ console.log(
45
+ 'PASS: discovery, model functions, messages and execution results match.',
46
+ );
47
+ console.log('PASS: name/input envelope uses the same workflow and execution contract.');
@@ -0,0 +1,75 @@
1
+ /** Tutorial tools: public in-memory notes only; no application database or network access. */
2
+ import { Support } from '@jangts/agent-core';
3
+
4
+ // The two business inputs differ; the model must use the selected tool's schema.
5
+ export function textInput(field) {
6
+ return Support.defineSchema({
7
+ jsonSchema: {
8
+ type: 'object',
9
+ properties: { [field]: { type: 'string', minLength: 1 } },
10
+ required: [field],
11
+ additionalProperties: false,
12
+ },
13
+ parse(value) {
14
+ if (
15
+ !value ||
16
+ typeof value !== 'object' ||
17
+ Array.isArray(value) ||
18
+ Object.keys(value).length !== 1 ||
19
+ typeof value[field] !== 'string' ||
20
+ !value[field].trim()
21
+ ) {
22
+ throw new TypeError(`Expected one nonempty string: ${field}`);
23
+ }
24
+ return { [field]: value[field].trim() };
25
+ },
26
+ });
27
+ }
28
+
29
+ const notes = [{ id: 'n1', title: '咖啡烘焙', body: '烘焙程度可结合豆色和风味判断。' }];
30
+
31
+ /** Build fresh declarations so manual and associated examples share no mutable request state. */
32
+ export function toolDefinitions() {
33
+ return [
34
+ {
35
+ id: 'search_notes',
36
+ asIntent: '用户希望查找笔记',
37
+ kind: 'read',
38
+ description: '按主题检索公开笔记',
39
+ inputSchema: textInput('query'),
40
+ authorize() {}, // This tutorial uses public constants, with no private account data.
41
+ execute({ query }) {
42
+ return notes
43
+ .filter((note) => note.title.includes(query))
44
+ .map(({ id, title }) => ({ id, title }));
45
+ },
46
+ parseOutput(value) {
47
+ if (
48
+ !Array.isArray(value) ||
49
+ value.some((note) => typeof note?.id !== 'string' || typeof note.title !== 'string')
50
+ ) {
51
+ throw new TypeError('Invalid search result');
52
+ }
53
+ return value;
54
+ },
55
+ },
56
+ {
57
+ id: 'read_note',
58
+ kind: 'read',
59
+ description: '读取已定位笔记的正文',
60
+ inputSchema: textInput('noteId'),
61
+ authorize() {},
62
+ execute({ noteId }) {
63
+ const note = notes.find((note) => note.id === noteId);
64
+ if (!note) throw new Error('Note not found');
65
+ return { ...note };
66
+ },
67
+ parseOutput(value) {
68
+ if (!value || typeof value.body !== 'string') {
69
+ throw new TypeError('Invalid note body');
70
+ }
71
+ return value;
72
+ },
73
+ },
74
+ ];
75
+ }