@koi-language/koi 1.0.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 (85) hide show
  1. package/QUICKSTART.md +89 -0
  2. package/README.md +545 -0
  3. package/examples/actions-demo.koi +177 -0
  4. package/examples/cache-test.koi +29 -0
  5. package/examples/calculator.koi +61 -0
  6. package/examples/clear-registry.js +33 -0
  7. package/examples/clear-registry.koi +30 -0
  8. package/examples/code-introspection-test.koi +149 -0
  9. package/examples/counter.koi +132 -0
  10. package/examples/delegation-test.koi +52 -0
  11. package/examples/directory-import-test.koi +84 -0
  12. package/examples/hello-world-claude.koi +52 -0
  13. package/examples/hello-world.koi +52 -0
  14. package/examples/hello.koi +24 -0
  15. package/examples/mcp-example.koi +70 -0
  16. package/examples/multi-event-handler-test.koi +144 -0
  17. package/examples/new-import-test.koi +89 -0
  18. package/examples/pipeline.koi +162 -0
  19. package/examples/registry-demo.koi +184 -0
  20. package/examples/registry-playbook-demo.koi +162 -0
  21. package/examples/registry-playbook-email-compositor-2.koi +140 -0
  22. package/examples/registry-playbook-email-compositor.koi +140 -0
  23. package/examples/sentiment.koi +90 -0
  24. package/examples/simple.koi +48 -0
  25. package/examples/skill-import-test.koi +76 -0
  26. package/examples/skills/advanced/index.koi +95 -0
  27. package/examples/skills/math-operations.koi +69 -0
  28. package/examples/skills/string-operations.koi +56 -0
  29. package/examples/task-chaining-demo.koi +244 -0
  30. package/examples/test-await.koi +22 -0
  31. package/examples/test-crypto-sha256.koi +196 -0
  32. package/examples/test-delegation.koi +41 -0
  33. package/examples/test-multi-team-routing.koi +258 -0
  34. package/examples/test-no-handler.koi +35 -0
  35. package/examples/test-npm-import.koi +67 -0
  36. package/examples/test-parse.koi +10 -0
  37. package/examples/test-peers-with-team.koi +59 -0
  38. package/examples/test-permissions-fail.koi +20 -0
  39. package/examples/test-permissions.koi +36 -0
  40. package/examples/test-simple-registry.koi +31 -0
  41. package/examples/test-typescript-import.koi +64 -0
  42. package/examples/test-uses-team-syntax.koi +25 -0
  43. package/examples/test-uses-team.koi +31 -0
  44. package/examples/utils/calculator.test.ts +144 -0
  45. package/examples/utils/calculator.ts +56 -0
  46. package/examples/utils/math-helpers.js +50 -0
  47. package/examples/utils/math-helpers.ts +55 -0
  48. package/examples/web-delegation-demo.koi +165 -0
  49. package/package.json +78 -0
  50. package/src/cli/koi.js +793 -0
  51. package/src/compiler/build-optimizer.js +447 -0
  52. package/src/compiler/cache-manager.js +274 -0
  53. package/src/compiler/import-resolver.js +369 -0
  54. package/src/compiler/parser.js +7542 -0
  55. package/src/compiler/transpiler.js +1105 -0
  56. package/src/compiler/typescript-transpiler.js +148 -0
  57. package/src/grammar/koi.pegjs +767 -0
  58. package/src/runtime/action-registry.js +172 -0
  59. package/src/runtime/actions/call-skill.js +45 -0
  60. package/src/runtime/actions/format.js +115 -0
  61. package/src/runtime/actions/print.js +42 -0
  62. package/src/runtime/actions/registry-delete.js +37 -0
  63. package/src/runtime/actions/registry-get.js +37 -0
  64. package/src/runtime/actions/registry-keys.js +33 -0
  65. package/src/runtime/actions/registry-search.js +34 -0
  66. package/src/runtime/actions/registry-set.js +50 -0
  67. package/src/runtime/actions/return.js +31 -0
  68. package/src/runtime/actions/send-message.js +58 -0
  69. package/src/runtime/actions/update-state.js +36 -0
  70. package/src/runtime/agent.js +1368 -0
  71. package/src/runtime/cli-logger.js +205 -0
  72. package/src/runtime/incremental-json-parser.js +201 -0
  73. package/src/runtime/index.js +33 -0
  74. package/src/runtime/llm-provider.js +1372 -0
  75. package/src/runtime/mcp-client.js +1171 -0
  76. package/src/runtime/planner.js +273 -0
  77. package/src/runtime/registry-backends/keyv-sqlite.js +215 -0
  78. package/src/runtime/registry-backends/local.js +260 -0
  79. package/src/runtime/registry.js +162 -0
  80. package/src/runtime/role.js +14 -0
  81. package/src/runtime/router.js +395 -0
  82. package/src/runtime/runtime.js +113 -0
  83. package/src/runtime/skill-selector.js +173 -0
  84. package/src/runtime/skill.js +25 -0
  85. package/src/runtime/team.js +162 -0
@@ -0,0 +1,90 @@
1
+ // ============================================================
2
+ // Koi — Sentiment Analysis Skill Demo
3
+ // Skill with agents using playbooks ONLY (LLM execution)
4
+ // ============================================================
5
+
6
+ package "demo.koi.sentiment"
7
+
8
+ role Worker { can execute, can propose }
9
+ role Reviewer { can critique, can approve }
10
+
11
+ Skill SentimentAnalysis {
12
+ affordance """
13
+ Analyzes text sentiment and returns positive/neutral/negative classification.
14
+ Uses 2 agents: Analyst (drafts analysis) + Reviewer (validates and normalizes).
15
+ Both agents use playbooks executed by LLM.
16
+ """
17
+
18
+ // Agent with ONLY playbook (LLM executes this)
19
+ Agent Analyst : Worker {
20
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, max_tokens: 300 }
21
+
22
+ on analyze(args: Json) {
23
+ playbook """
24
+ You are a sentiment analysis expert. Analyze the sentiment of the text provided in args.text.
25
+
26
+ Your task:
27
+ 1. Determine if the sentiment is positive, neutral, or negative
28
+ 2. Assign a confidence score between 0 and 1
29
+ 3. Provide a brief rationale (max 100 characters)
30
+
31
+ Return ONLY this exact JSON structure (no markdown, no explanations):
32
+ {
33
+ "label": "positive|neutral|negative",
34
+ "score": 0.85,
35
+ "rationale": "Brief explanation here"
36
+ }
37
+
38
+ Rules:
39
+ - Base your analysis ONLY on the provided text
40
+ - If ambiguous, use "neutral" with score ~0.5
41
+ - Keep rationale under 100 characters
42
+ """
43
+ }
44
+ }
45
+
46
+ // Agent with ONLY playbook (LLM executes this)
47
+ Agent Reviewer : Reviewer {
48
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.0, max_tokens: 200 }
49
+
50
+ on normalize(args: Json) {
51
+ playbook """
52
+ You are a quality reviewer. Validate and normalize the sentiment analysis draft in args.draft.
53
+
54
+ Your task:
55
+ 1. Ensure label is exactly "positive", "neutral", or "negative"
56
+ 2. Ensure score is a number between 0 and 1
57
+ 3. Ensure rationale is under 100 characters
58
+ 4. Fix any issues while preserving the original intent
59
+
60
+ Return ONLY this exact JSON structure (no markdown, no explanations):
61
+ {
62
+ "label": "positive|neutral|negative",
63
+ "score": 0.85,
64
+ "rationale": "Brief explanation here"
65
+ }
66
+
67
+ If the draft is missing fields, fill them conservatively (neutral, score 0.5).
68
+ """
69
+ }
70
+ }
71
+
72
+ Team Internal {
73
+ analyst = Analyst
74
+ reviewer = Reviewer
75
+ }
76
+
77
+ export async function run(input: any): Promise<any> {
78
+ const text = "I absolutely love this product! It works perfectly and exceeded all my expectations."
79
+
80
+ const draft =
81
+ await send Internal.event("analyze").role(Worker).any()({ text: text }) timeout 15s
82
+
83
+ const final =
84
+ await send Internal.event("normalize").role(Reviewer).any()({ draft: draft }) timeout 10s
85
+
86
+ return final
87
+ }
88
+ }
89
+
90
+ run SentimentAnalysis.run({})
@@ -0,0 +1,48 @@
1
+ // ============================================================
2
+ // Koi — Simple Example
3
+ // Minimal example showing agent communication with procedural code
4
+ // ============================================================
5
+
6
+ package "demo.koi.simple"
7
+
8
+ role Worker { can execute }
9
+ role Lead { can delegate }
10
+
11
+ // Agent with procedural code only (no playbook)
12
+ Agent Greeter : Worker {
13
+ on greet(args: Json) {
14
+ const name = args.name
15
+ return { message: "Hello, " + name + "!" }
16
+ }
17
+ }
18
+
19
+ // Agent with procedural code only (no playbook)
20
+ Agent Processor : Worker {
21
+ on process(args: Json) {
22
+ const msg = args.message
23
+ const upper = "PROCESSED: " + msg
24
+ return { result: upper }
25
+ }
26
+ }
27
+
28
+ Team SimpleTeam {
29
+ greeter = Greeter
30
+ processor = Processor
31
+ }
32
+
33
+ // Orchestrator with procedural code
34
+ Agent Orchestrator : Lead {
35
+ uses Team SimpleTeam
36
+
37
+ on start(args: Json) {
38
+ const greeting =
39
+ await send peers.event("greet").role(Worker).any()({ name: "Koi" }) timeout 5s
40
+
41
+ const processed =
42
+ await send peers.event("process").role(Worker).any()({ message: greeting.message }) timeout 5s
43
+
44
+ return processed
45
+ }
46
+ }
47
+
48
+ run Orchestrator.start({})
@@ -0,0 +1,76 @@
1
+ // ============================================================
2
+ // Skill Import Test
3
+ // Tests importing Skills from external files
4
+ // ============================================================
5
+
6
+ package "demo.koi.import"
7
+
8
+ // Import the Math Operations skill
9
+ import "./skills/math-operations.koi"
10
+
11
+ role Calculator { can calculate }
12
+
13
+ // Agent that uses the imported MathOperations skill
14
+ Agent MathAgent : Calculator {
15
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
16
+ uses Skill MathOperations
17
+
18
+ on calculate(args: Json) {
19
+ playbook """
20
+ Perform the following calculation: ${JSON.stringify(args)}
21
+
22
+ Use the available math tools to compute the result.
23
+ Return the result as JSON with the operation and result fields.
24
+ """
25
+ }
26
+ }
27
+
28
+ // Create a team
29
+ Team MathTeam {
30
+ calculator = MathAgent
31
+ }
32
+
33
+ // Demo runner
34
+ Agent DemoRunner : Calculator {
35
+ uses Team MathTeam
36
+
37
+ on runTest(args: Json) {
38
+ console.log("╔════════════════════════════════════════════╗")
39
+ console.log("║ Skill Import Test ║")
40
+ console.log("║ Testing imported Math Operations ║")
41
+ console.log("╚════════════════════════════════════════════╝\n")
42
+
43
+ // Test 1: Addition
44
+ console.log("📋 Test 1: Addition (5 + 3)")
45
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
46
+
47
+ const result1 =
48
+ await send peers.event("calculate").role(Calculator).any()({
49
+ operation: "add",
50
+ a: 5,
51
+ b: 3
52
+ }) timeout 10s
53
+
54
+ console.log("Result:", JSON.stringify(result1, null, 2))
55
+
56
+ // Test 2: Multiplication
57
+ console.log("\n📋 Test 2: Multiplication (7 × 6)")
58
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
59
+
60
+ const result2 =
61
+ await send peers.event("calculate").role(Calculator).any()({
62
+ operation: "multiply",
63
+ a: 7,
64
+ b: 6
65
+ }) timeout 10s
66
+
67
+ console.log("Result:", JSON.stringify(result2, null, 2))
68
+
69
+ return {
70
+ success: true,
71
+ tests_completed: 2
72
+ }
73
+ }
74
+ }
75
+
76
+ run DemoRunner.runTest({})
@@ -0,0 +1,95 @@
1
+ // ============================================================
2
+ // Advanced Skills Package
3
+ // Entry point that exports all skills from this directory
4
+ // ============================================================
5
+
6
+ package "skills.advanced"
7
+
8
+ // Re-export from other files in this directory
9
+ // (In a future version, we could support automatic re-export)
10
+
11
+ // For now, we define everything here as the main entry point
12
+
13
+ role DataScientist { can analyze }
14
+
15
+ // ============================================================
16
+ // Statistical Analysis Skill
17
+ // ============================================================
18
+
19
+ Skill StatisticalAnalysis {
20
+ affordance """
21
+ Perform statistical analysis including mean, median, standard deviation
22
+ """
23
+
24
+ export async function calculateMean(args: any): Promise<any> {
25
+ const numbers = args.numbers || [];
26
+ if (numbers.length === 0) {
27
+ return { error: "No numbers provided" };
28
+ }
29
+
30
+ const sum = numbers.reduce((a, b) => a + b, 0);
31
+ const mean = sum / numbers.length;
32
+
33
+ return {
34
+ operation: "mean",
35
+ numbers: numbers,
36
+ result: mean
37
+ };
38
+ }
39
+
40
+ export async function calculateMedian(args: any): Promise<any> {
41
+ const numbers = args.numbers || [];
42
+ if (numbers.length === 0) {
43
+ return { error: "No numbers provided" };
44
+ }
45
+
46
+ const sorted = [...numbers].sort((a, b) => a - b);
47
+ const mid = Math.floor(sorted.length / 2);
48
+ const median = sorted.length % 2 === 0
49
+ ? (sorted[mid - 1] + sorted[mid]) / 2
50
+ : sorted[mid];
51
+
52
+ return {
53
+ operation: "median",
54
+ numbers: numbers,
55
+ result: median
56
+ };
57
+ }
58
+
59
+ export async function calculateStdDev(args: any): Promise<any> {
60
+ const numbers = args.numbers || [];
61
+ if (numbers.length === 0) {
62
+ return { error: "No numbers provided" };
63
+ }
64
+
65
+ const mean = numbers.reduce((a, b) => a + b, 0) / numbers.length;
66
+ const variance = numbers.reduce((sum, num) => sum + Math.pow(num - mean, 2), 0) / numbers.length;
67
+ const stdDev = Math.sqrt(variance);
68
+
69
+ return {
70
+ operation: "stddev",
71
+ numbers: numbers,
72
+ mean: mean,
73
+ variance: variance,
74
+ result: stdDev
75
+ };
76
+ }
77
+ }
78
+
79
+ // ============================================================
80
+ // Data Analysis Agent
81
+ // ============================================================
82
+
83
+ Agent DataAnalyzer : DataScientist {
84
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 400 }
85
+ uses Skill StatisticalAnalysis
86
+
87
+ on analyze(args: Json) {
88
+ playbook """
89
+ Analyze this data: ${JSON.stringify(args)}
90
+
91
+ Use the available statistical tools to compute mean, median, and standard deviation.
92
+ Return a comprehensive analysis as JSON.
93
+ """
94
+ }
95
+ }
@@ -0,0 +1,69 @@
1
+ // ============================================================
2
+ // Math Operations Skill
3
+ // A reusable skill that provides math operations
4
+ // ============================================================
5
+
6
+ package "skills.math"
7
+
8
+ Skill MathOperations {
9
+ affordance """
10
+ Perform mathematical operations like addition, subtraction, multiplication, and division
11
+ """
12
+
13
+ export async function add(args: any): Promise<any> {
14
+ const a = args.a || 0;
15
+ const b = args.b || 0;
16
+ console.log(`[add] ${a} + ${b} = ${a + b}`);
17
+ return {
18
+ operation: "addition",
19
+ a: a,
20
+ b: b,
21
+ result: a + b
22
+ };
23
+ }
24
+
25
+ export async function subtract(args: any): Promise<any> {
26
+ const a = args.a || 0;
27
+ const b = args.b || 0;
28
+ console.log(`[subtract] ${a} - ${b} = ${a - b}`);
29
+ return {
30
+ operation: "subtraction",
31
+ a: a,
32
+ b: b,
33
+ result: a - b
34
+ };
35
+ }
36
+
37
+ export async function multiply(args: any): Promise<any> {
38
+ const a = args.a || 0;
39
+ const b = args.b || 0;
40
+ console.log(`[multiply] ${a} × ${b} = ${a * b}`);
41
+ return {
42
+ operation: "multiplication",
43
+ a: a,
44
+ b: b,
45
+ result: a * b
46
+ };
47
+ }
48
+
49
+ export async function divide(args: any): Promise<any> {
50
+ const a = args.a || 0;
51
+ const b = args.b || 1;
52
+ if (b === 0) {
53
+ console.error(`[divide] Error: Division by zero`);
54
+ return {
55
+ operation: "division",
56
+ a: a,
57
+ b: b,
58
+ error: "Division by zero"
59
+ };
60
+ }
61
+ console.log(`[divide] ${a} ÷ ${b} = ${a / b}`);
62
+ return {
63
+ operation: "division",
64
+ a: a,
65
+ b: b,
66
+ result: a / b
67
+ };
68
+ }
69
+ }
@@ -0,0 +1,56 @@
1
+ // ============================================================
2
+ // String Operations Skill
3
+ // A reusable skill for string manipulation
4
+ // ============================================================
5
+
6
+ package "skills.string"
7
+
8
+ Skill StringOperations {
9
+ affordance """
10
+ Perform string operations like uppercase, lowercase, reverse, and length
11
+ """
12
+
13
+ export async function toUpperCase(args: any): Promise<any> {
14
+ const text = args.text || "";
15
+ const result = text.toUpperCase();
16
+ console.log(`[toUpperCase] "${text}" → "${result}"`);
17
+ return {
18
+ operation: "toUpperCase",
19
+ original: text,
20
+ result: result
21
+ };
22
+ }
23
+
24
+ export async function toLowerCase(args: any): Promise<any> {
25
+ const text = args.text || "";
26
+ const result = text.toLowerCase();
27
+ console.log(`[toLowerCase] "${text}" → "${result}"`);
28
+ return {
29
+ operation: "toLowerCase",
30
+ original: text,
31
+ result: result
32
+ };
33
+ }
34
+
35
+ export async function reverse(args: any): Promise<any> {
36
+ const text = args.text || "";
37
+ const result = text.split('').reverse().join('');
38
+ console.log(`[reverse] "${text}" → "${result}"`);
39
+ return {
40
+ operation: "reverse",
41
+ original: text,
42
+ result: result
43
+ };
44
+ }
45
+
46
+ export async function getLength(args: any): Promise<any> {
47
+ const text = args.text || "";
48
+ const length = text.length;
49
+ console.log(`[getLength] "${text}" has ${length} characters`);
50
+ return {
51
+ operation: "length",
52
+ text: text,
53
+ length: length
54
+ };
55
+ }
56
+ }
@@ -0,0 +1,244 @@
1
+ // ============================================================
2
+ // Task Chaining Demo
3
+ // Demonstrates automatic output→input chaining between tasks
4
+ // ============================================================
5
+
6
+ package "demo.koi.chaining"
7
+
8
+ role Worker { can execute }
9
+ role Assistant { can help }
10
+
11
+ // ============================================================
12
+ // Specialized Worker Agents
13
+ // ============================================================
14
+
15
+ Agent FrenchTranslator : Worker {
16
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.2, max_tokens: 200 }
17
+
18
+ on translate(args: Json) {
19
+ playbook """
20
+ You are a specialized worker agent. Do NOT return actions, only direct results.
21
+
22
+ Translate the following text to French: ${JSON.stringify(args.text)}
23
+
24
+ Return ONLY valid JSON (NO actions field):
25
+ {
26
+ "translated": "text in French",
27
+ "original": "original text",
28
+ "language": "french"
29
+ }
30
+ """
31
+ }
32
+ }
33
+
34
+ Agent WordCounter : Worker {
35
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
36
+
37
+ on count(args: Json) {
38
+ playbook """
39
+ You are a specialized worker agent. Do NOT return actions, only direct results.
40
+
41
+ Count the number of words in this text: ${JSON.stringify(args.text)}
42
+
43
+ Return ONLY valid JSON (NO actions field):
44
+ {
45
+ "wordCount": <number>,
46
+ "text": "the text that was counted"
47
+ }
48
+ """
49
+ }
50
+ }
51
+
52
+ Agent TextFormatter : Worker {
53
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
54
+
55
+ on format(args: Json) {
56
+ playbook """
57
+ You are a specialized worker agent. Do NOT return actions, only direct results.
58
+
59
+ Convert this text to uppercase: ${JSON.stringify(args.text)}
60
+
61
+ Return ONLY valid JSON (NO actions field):
62
+ {
63
+ "formatted": "TEXT IN UPPERCASE",
64
+ "original": "original text"
65
+ }
66
+ """
67
+ }
68
+ }
69
+
70
+ Agent CharacterCounter : Worker {
71
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
72
+
73
+ on countChars(args: Json) {
74
+ playbook """
75
+ You are a specialized worker agent. Do NOT return actions, only direct results.
76
+
77
+ Count the number of characters in this text: ${JSON.stringify(args.text)}
78
+
79
+ Return ONLY valid JSON (NO actions field):
80
+ {
81
+ "charCount": <number>,
82
+ "text": "the text that was counted"
83
+ }
84
+ """
85
+ }
86
+ }
87
+
88
+ Agent DataValidator : Worker {
89
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
90
+
91
+ on validate(args: Json) {
92
+ playbook """
93
+ You are a specialized worker agent. Do NOT return actions, only direct results.
94
+
95
+ Validate this email: ${JSON.stringify(args.email)}
96
+
97
+ Check if it's a valid email format.
98
+
99
+ Return ONLY valid JSON (NO actions field):
100
+ {
101
+ "valid": true/false,
102
+ "email": "the email",
103
+ "message": "validation message"
104
+ }
105
+ """
106
+ }
107
+ }
108
+
109
+ // ============================================================
110
+ // Regular Assistant - NO special orchestrator knowledge
111
+ // ============================================================
112
+
113
+ Agent TaskAssistant : Assistant {
114
+ llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, max_tokens: 800 }
115
+
116
+ on helpUser(args: Json) {
117
+ playbook """
118
+ User request: ${args.request}
119
+
120
+ Help accomplish this request. If it requires multiple steps:
121
+ 1. Break it down into sequential tasks
122
+ 2. Chain outputs: use previous results as inputs for next tasks
123
+
124
+ The system will automatically:
125
+ - Execute each task
126
+ - Pass outputs to next task
127
+ - Route to appropriate team members
128
+
129
+ Return your plan or result as JSON.
130
+ """
131
+ }
132
+ }
133
+
134
+ // ============================================================
135
+ // Team
136
+ // ============================================================
137
+
138
+ Team AssistantTeam {
139
+ taskAssistant = TaskAssistant
140
+ }
141
+
142
+ // ============================================================
143
+ // Demo Runner
144
+ // ============================================================
145
+
146
+ Agent DemoRunner : Assistant {
147
+ uses Team AssistantTeam
148
+
149
+ on runDemo(args: Json) {
150
+ console.log("╔════════════════════════════════════════════╗")
151
+ console.log("║ Task Chaining Demo ║")
152
+ console.log("║ Output → Input Automatic Chaining ║")
153
+ console.log("╚════════════════════════════════════════════╝\n")
154
+
155
+ // ============================================================
156
+ // Test 1: Translate → Count Words
157
+ // ============================================================
158
+ console.log("📋 Test 1: Translate to French → Count Words")
159
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
160
+ console.log('Request: "Translate Hello world to French and then count the words"\n')
161
+
162
+ const test1 = await send peers.event("helpUser").role(Assistant).any()({
163
+ request: "Translate 'Hello world' to French and then count the words in the translation"
164
+ }) timeout 30s
165
+
166
+ console.log("✅ Result:")
167
+ console.log(JSON.stringify(test1, null, 2))
168
+ console.log("\nExpected flow:")
169
+ console.log(" 1. FrenchTranslator → { translated: 'Bonjour monde' }")
170
+ console.log(" 2. WordCounter(previousResult.translated) → { wordCount: 2 }")
171
+ console.log("\n")
172
+
173
+ // ============================================================
174
+ // Test 2: Format → Count Characters
175
+ // ============================================================
176
+ console.log("📋 Test 2: Format to Uppercase → Count Characters")
177
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
178
+ console.log('Request: "Format hello to uppercase and count characters"\n')
179
+
180
+ const test2 = await send peers.event("helpUser").role(Assistant).any()({
181
+ request: "Format the word hello to uppercase and then count the characters"
182
+ }) timeout 30s
183
+
184
+ console.log("✅ Result:")
185
+ console.log(JSON.stringify(test2, null, 2))
186
+ console.log("\nExpected flow:")
187
+ console.log(" 1. TextFormatter → { formatted: 'HELLO' }")
188
+ console.log(" 2. CharacterCounter(previousResult.formatted) → { charCount: 5 }")
189
+ console.log("\n")
190
+
191
+ // ============================================================
192
+ // Test 3: Chain of 3 Tasks
193
+ // ============================================================
194
+ console.log("📋 Test 3: Three-Step Chain")
195
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
196
+ console.log('Request: "Validate email, format it, count words"\n')
197
+
198
+ const test3 = await send peers.event("helpUser").role(Assistant).any()({
199
+ request: "Validate the email user@test.com, then format it to uppercase, then count how many words it has"
200
+ }) timeout 30s
201
+
202
+ console.log("✅ Result:")
203
+ console.log(JSON.stringify(test3, null, 2))
204
+ console.log("\nExpected flow:")
205
+ console.log(" 1. DataValidator → { valid: true, email: 'user@test.com' }")
206
+ console.log(" 2. TextFormatter(previousResult.email) → { formatted: 'USER@TEST.COM' }")
207
+ console.log(" 3. WordCounter(previousResult.formatted) → { wordCount: 1 }")
208
+ console.log("\n")
209
+
210
+ // ============================================================
211
+ // Test 4: Complex Original Request
212
+ // ============================================================
213
+ console.log("📋 Test 4: Your Original Example")
214
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
215
+ console.log('Request: "Traduce al frances y luego cuenta el numero de palabras"\n')
216
+
217
+ const test4 = await send peers.event("helpUser").role(Assistant).any()({
218
+ request: "Traduce 'artificial intelligence' al frances y luego cuenta el numero de palabras"
219
+ }) timeout 30s
220
+
221
+ console.log("✅ Result:")
222
+ console.log(JSON.stringify(test4, null, 2))
223
+ console.log("\nExpected flow:")
224
+ console.log(" 1. FrenchTranslator → { translated: 'intelligence artificielle' }")
225
+ console.log(" 2. WordCounter(previousResult.translated) → { wordCount: 2 }")
226
+ console.log("\n")
227
+
228
+ console.log("✅ All chaining tests completed!")
229
+ console.log("")
230
+ console.log("🔑 Key Points:")
231
+ console.log(" - LLM creates plan with chained actions")
232
+ console.log(" - Each action references previous results: $-{previousResult.field}")
233
+ console.log(" - Runtime resolves references before executing")
234
+ console.log(" - Output of action N becomes input of action N+1")
235
+ console.log(" - NO manual wiring needed!")
236
+
237
+ return {
238
+ success: true,
239
+ tests_completed: 4
240
+ }
241
+ }
242
+ }
243
+
244
+ run DemoRunner.runDemo({})