@crewai-ts/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 June
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,692 @@
1
+ # crewai-ts
2
+
3
+ TypeScript port of CrewAI.
4
+
5
+ ## Current port surface
6
+
7
+ - `Agent`, `Task`, `ConditionalTask`, `Crew`
8
+ - `LiteAgent` and `LiteAgentOutput` compatibility layer for direct agent execution
9
+ - `Flow` with standard TS `@start`, `@listen`, `@router`, `or_`, `and_`, `ask()` input providers, and `@humanFeedback`
10
+ - sequential `Crew.kickoff({ inputs })`
11
+ - `TaskOutput` and `CrewOutput`
12
+ - standard TS decorators: `@agent`, `@task`, `@crew`, `@beforeKickoff`, `@afterKickoff`, `@outputJson`, `@outputPydantic`, `@start`, `@listen`, `@router`
13
+ - `CrewProject` YAML/object config loading for `agentsConfig` and `tasksConfig`
14
+ - `BaseTool` / `StructuredTool` with argument validation, usage limits, tool-call execution, and task-level tool overrides
15
+ - automatic `read_file` tool for named task, crew, and agent input files
16
+ - tool result caching with `cacheFunction` and shareable `InMemoryToolCache`
17
+ - crew `cache: false` control for disabling library tool result caching
18
+ - sequential process async task scheduling, including sync barriers and CrewAI-style async validation
19
+ - typed `crewaiEventBus` lifecycle events for crew kickoff, task execution, tool usage, and failures
20
+ - agent and crew `stepCallback` hooks for tool/final agent steps
21
+ - crew-level `taskCallback` hooks after task callbacks, with duplicate callback suppression
22
+ - global before/after LLM and tool call hooks
23
+ - security `Fingerprint` / `SecurityConfig` on agents, crews, and tasks
24
+ - checkpoint `CheckpointConfig`, filesystem `JsonProvider`, and SQLite `SqliteProvider`
25
+ - state `EventRecord` / `EventNode` graph for event relationship tracking
26
+ - state `RuntimeState` checkpoint serialization, restore, lineage, and fork helpers
27
+ - crew `outputLogFile` task execution logs in text or JSON files
28
+ - crew `executionLogs` and `taskExecutionOutputJsonFiles` for per-task audit records
29
+ - crew `replay(taskRef, inputs?)` from a task id, name, index, or task object
30
+ - `Memory` / `MemoryScope` with recall/save tools injected into crews when memory is enabled
31
+ - `Knowledge` sources (`StringKnowledgeSource`, `TextFileKnowledgeSource`, `JSONKnowledgeSource`, `CSVKnowledgeSource`) with agent and crew context injection
32
+ - hierarchical process with manager agent/manager LLM validation and coworker delegation tools
33
+ - sequential `allowDelegation` agents with coworker delegate/question tools
34
+ - function or object LLM providers with tool-call options, string model registry, and token usage aggregation
35
+ - iterative agent tool-use loop with `maxIter` and `resultAsAnswer` support
36
+ - agent `maxRetryLimit` retries around task execution failures
37
+ - agent `maxExecutionTime` timeout enforcement for task execution
38
+ - agent `useSystemPrompt` control for models that do not accept system-role messages
39
+ - deprecated CrewAI agent compatibility fields: `allowCodeExecution`, `codeExecutionMode`, `respectContextWindow`, `multimodal`
40
+ - agent `systemTemplate`, `promptTemplate`, and `responseTemplate` prompt rendering
41
+ - agent `injectDate` / `dateFormat` prompt injection
42
+ - callable agent `guardrail` with retry-limit enforcement
43
+ - agent-level `PlanningConfig`, `planning`, and legacy `reasoning` compatibility
44
+ - agent and crew `maxRpm` throttling for LLM calls
45
+ - `kickoffForEach` / `kickoffForEachAsync` batch execution with aggregate usage metrics
46
+ - crew-level planning that injects per-task execution plans before kickoff
47
+ - CrewAI-style default task context aggregation from previous task outputs
48
+ - task `outputFile` writing with input interpolation and safe path validation
49
+ - task `inputFiles` / `input_files` text file prompt injection
50
+ - task `outputConverter` / `converter_cls` hooks for structured output conversion
51
+ - structured task interpolation for strings, numbers, booleans, arrays, objects, and `null`
52
+ - single or ordered multiple task `guardrails` with retry support
53
+ - task `humanInput` feedback loops with injectable providers
54
+ - task execution counters: `usedTools`, `toolsErrors`, `delegations`, `promptContext`, `processedByAgents`
55
+ - task `allowCrewaiTriggerContext` support for `crewai_trigger_payload` kickoff inputs
56
+ - `ConditionalTask` skip logic based on the previous task output
57
+ - basic `stream: true` crew and flow outputs via `CrewStreamingOutput` / `FlowStreamingOutput`
58
+
59
+ Decorators store only library-private metadata. They do not use
60
+ `reflect-metadata`, parameter decorators, or Nest metadata, so Nest applications
61
+ should consume this package as a normal TypeScript library and keep Nest DI
62
+ separate.
63
+
64
+ CrewAI Python-style snake_case aliases are available for common async entry
65
+ points, including `kickoff_async`, `kickoff_for_each`,
66
+ `kickoff_for_each_async`, `akickoff_for_each`, `resume_async`, `from_pending`,
67
+ and `from_state`.
68
+
69
+ ```ts
70
+ import { Agent, Crew, Process, Task, agent, crew, task } from "@crewai-ts/core";
71
+
72
+ class ResearchCrew {
73
+ @agent
74
+ researcher() {
75
+ return new Agent({
76
+ role: "Researcher",
77
+ goal: "Find facts",
78
+ backstory: "Careful analyst",
79
+ llm: (messages) => `result: ${messages.at(-1)?.content ?? ""}`,
80
+ });
81
+ }
82
+
83
+ @task
84
+ researchTask() {
85
+ return new Task({
86
+ description: "Research {topic}",
87
+ expectedOutput: "A concise brief",
88
+ agent: this.researcher(),
89
+ });
90
+ }
91
+
92
+ @crew
93
+ crew() {
94
+ return new Crew({ process: Process.sequential });
95
+ }
96
+ }
97
+
98
+ const result = await new ResearchCrew().crew().kickoff({
99
+ inputs: { topic: "CrewAI" },
100
+ });
101
+
102
+ const batchResults = await new ResearchCrew().crew().kickoffForEach({
103
+ inputs: [{ topic: "CrewAI" }, { topic: "TypeScript" }],
104
+ });
105
+ ```
106
+
107
+ ## Streaming
108
+
109
+ Set `stream: true` on a crew or flow to receive a streaming output wrapper from
110
+ `kickoff()`. The current TypeScript port exposes the final output as an async
111
+ stream chunk and makes the complete result available after iteration.
112
+
113
+ ```ts
114
+ import { CrewStreamingOutput } from "@crewai-ts/core";
115
+
116
+ const streaming = await crew.kickoff() as unknown as CrewStreamingOutput;
117
+
118
+ for await (const chunk of streaming) {
119
+ console.log(chunk.content);
120
+ }
121
+
122
+ console.log(streaming.result.raw);
123
+ ```
124
+
125
+ ## LiteAgent
126
+
127
+ `LiteAgent` mirrors CrewAI's deprecated lightweight direct-execution API while
128
+ reusing the main `Agent` runtime internally. It returns `LiteAgentOutput`, keeps
129
+ the executed messages, exposes usage metrics, and supports the common
130
+ snake_case aliases.
131
+
132
+ ```ts
133
+ import { LiteAgent } from "@crewai-ts/core";
134
+
135
+ const agent = new LiteAgent({
136
+ role: "Research Assistant",
137
+ goal: "Answer quickly",
138
+ backstory: "A concise research helper",
139
+ llm: (messages) => `answer: ${messages.at(-1)?.content ?? ""}`,
140
+ });
141
+
142
+ const output = await agent.kickoff_async("What is CrewAI?");
143
+ console.log(output.raw);
144
+ ```
145
+
146
+ ## Hooks
147
+
148
+ Register global hooks to inspect, mutate, or block LLM and tool calls. Hook
149
+ contexts expose CrewAI-compatible camelCase and snake_case fields where useful.
150
+
151
+ ```ts
152
+ import { afterLlmCall, beforeToolCall } from "@crewai-ts/core";
153
+
154
+ afterLlmCall((context) => {
155
+ if (typeof context.response === "string") {
156
+ return context.response.replace("SECRET", "[redacted]");
157
+ }
158
+ return null;
159
+ });
160
+
161
+ beforeToolCall((context) => {
162
+ if (context.tool_name === "delete_file") {
163
+ return false;
164
+ }
165
+ return null;
166
+ });
167
+ ```
168
+
169
+ ## Security
170
+
171
+ Agents, crews, and tasks expose a `fingerprint` through `SecurityConfig` for
172
+ identity, auditing, and deterministic seed-based identifiers.
173
+
174
+ ```ts
175
+ import { Agent, Fingerprint, SecurityConfig } from "@crewai-ts/core";
176
+
177
+ const securityConfig = new SecurityConfig({
178
+ fingerprint: Fingerprint.generate("research-agent", { version: "1.0" }),
179
+ });
180
+
181
+ const agent = new Agent({
182
+ role: "Researcher",
183
+ goal: "Find facts",
184
+ backstory: "Careful analyst",
185
+ securityConfig,
186
+ });
187
+
188
+ console.log(agent.fingerprint.uuid_str);
189
+ ```
190
+
191
+ ## Checkpoints
192
+
193
+ `CheckpointConfig`, `JsonProvider`, and `SqliteProvider` provide CrewAI-compatible
194
+ checkpoint configuration and checkpoint storage. Agents, crews, and flows accept
195
+ a `checkpoint` option.
196
+
197
+ ```ts
198
+ import { CheckpointConfig, JsonProvider, SqliteProvider } from "@crewai-ts/core";
199
+
200
+ const checkpoint = new CheckpointConfig({
201
+ location: ".checkpoints",
202
+ onEvents: ["task_completed"],
203
+ provider: new JsonProvider(),
204
+ });
205
+
206
+ const sqliteCheckpoint = new CheckpointConfig({
207
+ location: ".checkpoints.db",
208
+ provider: new SqliteProvider(),
209
+ });
210
+ ```
211
+
212
+ ## Tools
213
+
214
+ Tools can be attached to agents or tasks. Task tools take precedence during that
215
+ task, matching CrewAI's task-level override behavior.
216
+
217
+ ```ts
218
+ import { Agent, StructuredTool, Task } from "@crewai-ts/core";
219
+
220
+ const search = new StructuredTool({
221
+ name: "search",
222
+ description: "Search for a topic",
223
+ argsSchema: {
224
+ query: { type: "string", required: true },
225
+ },
226
+ maxUsageCount: 3,
227
+ func: ({ query }) => `found ${String(query)}`,
228
+ });
229
+
230
+ const researcher = new Agent({
231
+ role: "Researcher",
232
+ goal: "Find facts",
233
+ backstory: "Careful analyst",
234
+ tools: [search],
235
+ maxRpm: 30,
236
+ stepCallback: (step) => {
237
+ console.log(step.type, step.output);
238
+ },
239
+ llm: () => ({ toolName: "search", arguments: { query: "CrewAI" } }),
240
+ });
241
+
242
+ const task = new Task({
243
+ description: "Research {topic}",
244
+ expectedOutput: "A concise brief",
245
+ agent: researcher,
246
+ guardrails: [
247
+ (output) => [output.raw.length > 0, output.raw],
248
+ ],
249
+ });
250
+ ```
251
+
252
+ When an LLM returns a tool call, the agent executes the tool, appends the tool
253
+ result to the message list, and calls the LLM again until it returns a final
254
+ answer or reaches `maxIter`. Tools marked `resultAsAnswer` return their tool
255
+ output directly. Set `functionCallingLlm` on an `Agent` or `Crew` when tool-call
256
+ selection should use a separate model from the main answer-generating LLM.
257
+
258
+ Tools cache successful outputs by normalized arguments. Use `cacheFunction` to
259
+ skip selected writes, or pass a shared `InMemoryToolCache` to reuse cached
260
+ results across tool instances.
261
+
262
+ ## LLM providers
263
+
264
+ Agents accept either a function LLM, an object provider with `call()`, or a
265
+ registered model name. Function LLMs receive the message list and call options;
266
+ object providers can expose `getUsageMetrics()` or CrewAI-style
267
+ `getTokenUsageSummary()` for exact token accounting. When they do not, the
268
+ runtime records an estimated usage count.
269
+
270
+ ```ts
271
+ import { Agent, registerLLMProvider } from "@crewai-ts/core";
272
+
273
+ registerLLMProvider("local/research", {
274
+ call: async (messages, { tools } = {}) => {
275
+ return `tools available: ${tools?.map((tool) => tool.name).join(", ") ?? "none"}`;
276
+ },
277
+ getUsageMetrics: () => ({
278
+ totalTokens: 12,
279
+ promptTokens: 8,
280
+ cachedPromptTokens: 0,
281
+ completionTokens: 4,
282
+ reasoningTokens: 0,
283
+ cacheCreationTokens: 0,
284
+ successfulRequests: 1,
285
+ }),
286
+ });
287
+
288
+ const researcher = new Agent({
289
+ role: "Researcher",
290
+ goal: "Find facts",
291
+ backstory: "Careful analyst",
292
+ llm: "local/research",
293
+ });
294
+
295
+ await researcher.kickoff("Summarize the notes", {
296
+ inputFiles: {
297
+ notes: "docs/notes.txt",
298
+ },
299
+ });
300
+
301
+ await researcher.kickoff([
302
+ {
303
+ role: "user",
304
+ content: "Summarize the uploaded notes",
305
+ files: {
306
+ notes: "docs/notes.txt",
307
+ },
308
+ },
309
+ ]);
310
+ ```
311
+
312
+ ## Agent planning
313
+
314
+ Agents can create a reasoning plan before executing a task. `planning: true`
315
+ uses a bounded low-effort default config, while `PlanningConfig` exposes the
316
+ custom prompt and limit knobs.
317
+
318
+ ```ts
319
+ import { Agent, PlanningConfig } from "@crewai-ts/core";
320
+
321
+ const researcher = new Agent({
322
+ role: "Researcher",
323
+ goal: "Find facts",
324
+ backstory: "Careful analyst",
325
+ llm: "local/research",
326
+ planningConfig: new PlanningConfig({
327
+ maxSteps: 10,
328
+ planPrompt: "Plan this task: {description}",
329
+ }),
330
+ });
331
+
332
+ const answer = await researcher.kickoff("Research CrewAI");
333
+ ```
334
+
335
+ ## Flows
336
+
337
+ Flows run decorated methods as a stateful workflow. `@start` methods begin the
338
+ run, `@listen` methods react to completed methods or router path strings, and
339
+ `@router` methods return the next path label.
340
+
341
+ ```ts
342
+ import { Flow, and_, listen, router, start } from "@crewai-ts/core";
343
+
344
+ class ResearchFlow extends Flow<{ topic?: string; done?: boolean }> {
345
+ @start()
346
+ begin(inputs: { topic: string }) {
347
+ this.state.topic = inputs.topic;
348
+ return inputs.topic;
349
+ }
350
+
351
+ @router("begin")
352
+ route() {
353
+ return this.state.topic ? "research" : "skip";
354
+ }
355
+
356
+ @listen(and_("research", "begin"))
357
+ finish() {
358
+ this.state.done = true;
359
+ return `researched ${this.state.topic}`;
360
+ }
361
+ }
362
+
363
+ const result = await new ResearchFlow().kickoff({
364
+ inputs: { topic: "CrewAI" },
365
+ });
366
+ ```
367
+
368
+ `@start("path")` is also supported for conditional starts after a method or
369
+ router path fires. Flow execution is bounded by `maxMethodCalls` so cyclic
370
+ flows fail clearly instead of running forever.
371
+
372
+ Inside a flow, use `this.kickoffCrew(crew)` to run a crew with the flow's
373
+ `inputFiles` / `input_files` automatically forwarded.
374
+
375
+ Flows can request user input through `this.ask()`. Set an `inputProvider` on
376
+ the flow instance or `flowConfig.inputProvider` globally. Providers may return
377
+ a string, `null`, or `{ text, metadata }`; responses are available through
378
+ `flow.inputHistory`.
379
+
380
+ ```ts
381
+ const flow = new ResearchFlow({
382
+ inputProvider: {
383
+ requestInput: async (_message, _flow, metadata) => ({
384
+ text: "CrewAI",
385
+ metadata: { source: metadata?.channel },
386
+ }),
387
+ },
388
+ });
389
+
390
+ const topic = await flow.ask("Topic?", {
391
+ metadata: { channel: "research" },
392
+ timeout: 30,
393
+ });
394
+ ```
395
+
396
+ Flow methods can also be wrapped with `@humanFeedback`. The method output is
397
+ sent to a feedback provider, the result is stored on
398
+ `flow.lastHumanFeedback` / `flow.humanFeedbackHistory`, and `emit` values make
399
+ the method act as a router.
400
+
401
+ ```ts
402
+ class ReviewFlow extends Flow {
403
+ @start()
404
+ @humanFeedback({
405
+ message: "Review this draft",
406
+ emit: ["approved", "rejected"],
407
+ provider: {
408
+ requestFeedback: async () => "approved",
409
+ },
410
+ })
411
+ draft() {
412
+ return "Draft content";
413
+ }
414
+
415
+ @listen("approved")
416
+ publish() {
417
+ return this.lastHumanFeedback?.output;
418
+ }
419
+ }
420
+ ```
421
+
422
+ Providers that hand off review to an external system can throw
423
+ `HumanFeedbackPending`. `kickoff()` returns that object, emits
424
+ `method_execution_paused` and `flow_paused`, and does not treat the pause as a
425
+ Flow failure. The same Flow instance can continue with `resume(feedback)` or
426
+ `resumeAsync(feedback)`, which records `lastHumanFeedback` and resumes any
427
+ listeners waiting on the paused method or emitted outcome.
428
+
429
+ ```ts
430
+ provider: {
431
+ requestFeedback: (context) => {
432
+ throw new HumanFeedbackPending({
433
+ context,
434
+ callbackInfo: { ticketId: "review-123" },
435
+ });
436
+ },
437
+ }
438
+ ```
439
+
440
+ To resume after process restart, provide a `JsonFlowPersistence` in the Flow
441
+ constructor. Pending feedback is written with the current state and can be
442
+ restored with `Flow.fromPending(flowId, persistence)`.
443
+
444
+ ```ts
445
+ const persistence = new JsonFlowPersistence(".flows");
446
+ const pending = await flow.kickoff();
447
+
448
+ if (pending instanceof HumanFeedbackPending && pending.context.flowId) {
449
+ const restored = await ReviewFlow.fromPending(pending.context.flowId, persistence);
450
+ await restored.resume("approved");
451
+ }
452
+ ```
453
+
454
+ The same persistence object stores ordinary Flow state after each completed
455
+ method. Use `Flow.fromState(flowId, persistence)` to restore the latest state
456
+ snapshot for a Flow id.
457
+
458
+ After a run, `flow.methodOutputs`, `flow.completedMethods`,
459
+ `flow.methodExecutionCounts`, and `flow.executionTrace` expose the last
460
+ execution's method-level runtime state.
461
+
462
+ Use `getFlowStructure(flowOrClass)` to inspect the static Flow graph for
463
+ visualization or tooling.
464
+
465
+ Use `flow.toExecutionData()` and `flow.reload(data)` to export and restore the
466
+ last run's state, completed methods, method outputs, and execution trace.
467
+ Flows emit `flow_started`, `flow_input_requested`, `flow_input_received`,
468
+ `human_feedback_requested`, `human_feedback_received`,
469
+ `method_execution_started`, `method_execution_finished`,
470
+ `method_execution_failed`, `method_execution_paused`, `flow_finished`,
471
+ `flow_failed`, and `flow_paused` events on `crewaiEventBus`.
472
+
473
+ ## Task output files
474
+
475
+ Tasks can persist their final output to a file. Paths support the same input
476
+ interpolation as task descriptions, and directories are created by default.
477
+
478
+ ```ts
479
+ const report = new Task({
480
+ description: "Research {topic}",
481
+ expectedOutput: "A concise brief",
482
+ agent: researcher,
483
+ outputFile: "reports/{topic}.md",
484
+ });
485
+ ```
486
+
487
+ ## Task input files
488
+
489
+ Tasks can attach named text input files. The runtime loads their content into
490
+ the task prompt so function LLMs and text-only providers can consume the same
491
+ named file surface. When input files are present, the runtime also exposes a
492
+ `read_file` tool that accepts `{ file_name: "notes" }`.
493
+
494
+ ```ts
495
+ const task = new Task({
496
+ description: "Summarize the provided notes",
497
+ expectedOutput: "A concise summary",
498
+ agent: researcher,
499
+ inputFiles: {
500
+ notes: "docs/notes.txt",
501
+ inline: {
502
+ filename: "brief.md",
503
+ contentType: "text/markdown",
504
+ content: "# Brief\nSummarize this.",
505
+ },
506
+ },
507
+ });
508
+
509
+ const result = await new Crew({ agents: [researcher], tasks: [task] }).kickoff({
510
+ inputFiles: {
511
+ sharedNotes: "docs/shared-notes.txt",
512
+ },
513
+ });
514
+ ```
515
+
516
+ Structured file objects passed through `kickoff({ inputs })` are extracted into
517
+ the same input-file surface and removed from interpolation inputs. Raw string
518
+ inputs are left untouched, so normal values such as `"docs/notes.txt"` are not
519
+ treated as files unless passed through `inputFiles` / `input_files`.
520
+
521
+ ## Conditional tasks
522
+
523
+ `ConditionalTask` evaluates the previous task output before running. When its
524
+ condition returns false, the crew records an empty raw task output and continues.
525
+
526
+ ```ts
527
+ import { ConditionalTask } from "@crewai-ts/core";
528
+
529
+ const followUp = new ConditionalTask({
530
+ description: "Write follow-up details",
531
+ expectedOutput: "Only needed when the previous task asks for more detail",
532
+ agent: researcher,
533
+ condition: (output) => output.raw.includes("needs follow-up"),
534
+ });
535
+ ```
536
+
537
+ ## Human input
538
+
539
+ Set `humanInput` on a task to request feedback after the first output. Empty
540
+ feedback accepts the output; non-empty feedback is appended to the next prompt
541
+ and the task reruns. Server apps should inject their own provider instead of
542
+ using terminal input.
543
+
544
+ ```ts
545
+ const crew = new Crew({
546
+ agents: [reviewer],
547
+ tasks: [
548
+ new Task({
549
+ description: "Review the report",
550
+ expectedOutput: "Approved report",
551
+ agent: reviewer,
552
+ humanInput: true,
553
+ }),
554
+ ],
555
+ humanInputProvider: {
556
+ requestFeedback: async ({ output }) => {
557
+ return output.raw.includes("approved") ? "" : "Please include approval.";
558
+ },
559
+ },
560
+ });
561
+ ```
562
+
563
+ ## Crew planning
564
+
565
+ Enable `planning` to run a planner LLM before task execution. The planner returns
566
+ one plan per task, and each task prompt receives its current plan without
567
+ mutating the original task description.
568
+
569
+ ```ts
570
+ const crew = new Crew({
571
+ agents: [researcher],
572
+ tasks: [report],
573
+ planning: true,
574
+ planningLlm: "gpt-4o-mini",
575
+ });
576
+ ```
577
+
578
+ ## Memory
579
+
580
+ Enable memory on a crew to append relevant memory context to task prompts and
581
+ inject recall/save tools into task execution. Agent-level memory is also
582
+ available through `new Agent({ memory })` and stores completed agent results.
583
+
584
+ ```ts
585
+ import { Agent, Crew, Memory, Process, Task } from "@crewai-ts/core";
586
+
587
+ const memory = new Memory();
588
+ memory.remember("CrewAI supports sequential crews");
589
+
590
+ const researcher = new Agent({
591
+ role: "Researcher",
592
+ goal: "Use memory",
593
+ backstory: "Careful analyst",
594
+ llm: () => ({
595
+ toolName: "Search_memory",
596
+ arguments: { queries: ["sequential crews"] },
597
+ }),
598
+ });
599
+
600
+ const crew = new Crew({
601
+ agents: [researcher],
602
+ tasks: [
603
+ new Task({
604
+ description: "Recall CrewAI facts",
605
+ expectedOutput: "Relevant memories",
606
+ agent: researcher,
607
+ }),
608
+ ],
609
+ process: Process.sequential,
610
+ memory,
611
+ });
612
+ ```
613
+
614
+ ## Knowledge
615
+
616
+ Attach `Knowledge` or `knowledgeSources` to an agent or crew to inject relevant
617
+ source snippets into task prompts as additional information.
618
+
619
+ ```ts
620
+ import { Agent, Crew, StringKnowledgeSource, TextFileKnowledgeSource, Task } from "@crewai-ts/core";
621
+
622
+ const researcher = new Agent({
623
+ role: "Researcher",
624
+ goal: "Use knowledge",
625
+ backstory: "Careful analyst",
626
+ llm: (messages) => messages.at(-1)?.content ?? "",
627
+ });
628
+
629
+ const crew = new Crew({
630
+ agents: [researcher],
631
+ tasks: [
632
+ new Task({
633
+ description: "Explain Nest integration",
634
+ expectedOutput: "Integration guidance",
635
+ agent: researcher,
636
+ }),
637
+ ],
638
+ knowledgeSources: [
639
+ new StringKnowledgeSource("Nest should consume crewai-ts as a normal TypeScript library."),
640
+ new TextFileKnowledgeSource("knowledge/nest-notes.txt"),
641
+ ],
642
+ });
643
+
644
+ crew.resetMemories("knowledge");
645
+ ```
646
+
647
+ ## YAML-backed project config
648
+
649
+ `CrewProject` mirrors CrewAI's `agents.yaml` / `tasks.yaml` workflow. String
650
+ references in config are resolved only against this library's decorated methods.
651
+
652
+ ```ts
653
+ import {
654
+ Agent,
655
+ Crew,
656
+ CrewProject,
657
+ Process,
658
+ Task,
659
+ agent,
660
+ agentOptionsFromConfig,
661
+ crew,
662
+ task,
663
+ taskOptionsFromConfig,
664
+ } from "@crewai-ts/core";
665
+
666
+ class ResearchCrew extends CrewProject {
667
+ agentsConfig = "config/agents.yaml";
668
+ tasksConfig = "config/tasks.yaml";
669
+
670
+ @agent
671
+ researcher() {
672
+ return new Agent(agentOptionsFromConfig(this.agentConfig("researcher")));
673
+ }
674
+
675
+ @task
676
+ researchTask() {
677
+ return new Task(taskOptionsFromConfig(this.taskConfig("researchTask")));
678
+ }
679
+
680
+ @crew
681
+ crew() {
682
+ return new Crew({ process: Process.sequential });
683
+ }
684
+ }
685
+ ```
686
+
687
+ ## Scripts
688
+
689
+ - `npm run build` builds ESM output and declarations.
690
+ - `npm run check` runs TypeScript in no-emit mode.
691
+ - `npm test` runs Vitest.
692
+ - `npm run lint` runs ESLint.