@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.
- package/QUICKSTART.md +89 -0
- package/README.md +545 -0
- package/examples/actions-demo.koi +177 -0
- package/examples/cache-test.koi +29 -0
- package/examples/calculator.koi +61 -0
- package/examples/clear-registry.js +33 -0
- package/examples/clear-registry.koi +30 -0
- package/examples/code-introspection-test.koi +149 -0
- package/examples/counter.koi +132 -0
- package/examples/delegation-test.koi +52 -0
- package/examples/directory-import-test.koi +84 -0
- package/examples/hello-world-claude.koi +52 -0
- package/examples/hello-world.koi +52 -0
- package/examples/hello.koi +24 -0
- package/examples/mcp-example.koi +70 -0
- package/examples/multi-event-handler-test.koi +144 -0
- package/examples/new-import-test.koi +89 -0
- package/examples/pipeline.koi +162 -0
- package/examples/registry-demo.koi +184 -0
- package/examples/registry-playbook-demo.koi +162 -0
- package/examples/registry-playbook-email-compositor-2.koi +140 -0
- package/examples/registry-playbook-email-compositor.koi +140 -0
- package/examples/sentiment.koi +90 -0
- package/examples/simple.koi +48 -0
- package/examples/skill-import-test.koi +76 -0
- package/examples/skills/advanced/index.koi +95 -0
- package/examples/skills/math-operations.koi +69 -0
- package/examples/skills/string-operations.koi +56 -0
- package/examples/task-chaining-demo.koi +244 -0
- package/examples/test-await.koi +22 -0
- package/examples/test-crypto-sha256.koi +196 -0
- package/examples/test-delegation.koi +41 -0
- package/examples/test-multi-team-routing.koi +258 -0
- package/examples/test-no-handler.koi +35 -0
- package/examples/test-npm-import.koi +67 -0
- package/examples/test-parse.koi +10 -0
- package/examples/test-peers-with-team.koi +59 -0
- package/examples/test-permissions-fail.koi +20 -0
- package/examples/test-permissions.koi +36 -0
- package/examples/test-simple-registry.koi +31 -0
- package/examples/test-typescript-import.koi +64 -0
- package/examples/test-uses-team-syntax.koi +25 -0
- package/examples/test-uses-team.koi +31 -0
- package/examples/utils/calculator.test.ts +144 -0
- package/examples/utils/calculator.ts +56 -0
- package/examples/utils/math-helpers.js +50 -0
- package/examples/utils/math-helpers.ts +55 -0
- package/examples/web-delegation-demo.koi +165 -0
- package/package.json +78 -0
- package/src/cli/koi.js +793 -0
- package/src/compiler/build-optimizer.js +447 -0
- package/src/compiler/cache-manager.js +274 -0
- package/src/compiler/import-resolver.js +369 -0
- package/src/compiler/parser.js +7542 -0
- package/src/compiler/transpiler.js +1105 -0
- package/src/compiler/typescript-transpiler.js +148 -0
- package/src/grammar/koi.pegjs +767 -0
- package/src/runtime/action-registry.js +172 -0
- package/src/runtime/actions/call-skill.js +45 -0
- package/src/runtime/actions/format.js +115 -0
- package/src/runtime/actions/print.js +42 -0
- package/src/runtime/actions/registry-delete.js +37 -0
- package/src/runtime/actions/registry-get.js +37 -0
- package/src/runtime/actions/registry-keys.js +33 -0
- package/src/runtime/actions/registry-search.js +34 -0
- package/src/runtime/actions/registry-set.js +50 -0
- package/src/runtime/actions/return.js +31 -0
- package/src/runtime/actions/send-message.js +58 -0
- package/src/runtime/actions/update-state.js +36 -0
- package/src/runtime/agent.js +1368 -0
- package/src/runtime/cli-logger.js +205 -0
- package/src/runtime/incremental-json-parser.js +201 -0
- package/src/runtime/index.js +33 -0
- package/src/runtime/llm-provider.js +1372 -0
- package/src/runtime/mcp-client.js +1171 -0
- package/src/runtime/planner.js +273 -0
- package/src/runtime/registry-backends/keyv-sqlite.js +215 -0
- package/src/runtime/registry-backends/local.js +260 -0
- package/src/runtime/registry.js +162 -0
- package/src/runtime/role.js +14 -0
- package/src/runtime/router.js +395 -0
- package/src/runtime/runtime.js +113 -0
- package/src/runtime/skill-selector.js +173 -0
- package/src/runtime/skill.js +25 -0
- package/src/runtime/team.js +162 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Test that permissions are enforced
|
|
2
|
+
package "test.permissions.fail"
|
|
3
|
+
|
|
4
|
+
// Role without execute permission
|
|
5
|
+
role ReadOnly { can registry_read }
|
|
6
|
+
|
|
7
|
+
// Agent without execute permission - should fail when trying to print
|
|
8
|
+
Agent ReadOnlyAgent : ReadOnly {
|
|
9
|
+
llm default = { provider: "openai", model: "gpt-4o-mini" }
|
|
10
|
+
|
|
11
|
+
on test(args: Json) {
|
|
12
|
+
playbook """
|
|
13
|
+
Try to print a message (should fail due to missing execute permission).
|
|
14
|
+
|
|
15
|
+
Show: "❌ This should not appear because print requires execute permission"
|
|
16
|
+
"""
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
run ReadOnlyAgent.test({})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Test permissions system
|
|
2
|
+
package "test.permissions"
|
|
3
|
+
|
|
4
|
+
// Role with execute permission
|
|
5
|
+
role Worker { can execute }
|
|
6
|
+
|
|
7
|
+
// Role without execute permission (empty)
|
|
8
|
+
role ReadOnly { can registry_read }
|
|
9
|
+
|
|
10
|
+
// Agent with execute permission - should work
|
|
11
|
+
Agent TestAgent : Worker {
|
|
12
|
+
llm default = { provider: "openai", model: "gpt-4o-mini" }
|
|
13
|
+
|
|
14
|
+
on test(args: Json) {
|
|
15
|
+
playbook """
|
|
16
|
+
Print a simple message to test permissions.
|
|
17
|
+
|
|
18
|
+
Show: "✅ Print action works with execute permission"
|
|
19
|
+
"""
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Agent without execute permission - should fail
|
|
24
|
+
Agent ReadOnlyAgent : ReadOnly {
|
|
25
|
+
llm default = { provider: "openai", model: "gpt-4o-mini" }
|
|
26
|
+
|
|
27
|
+
on test(args: Json) {
|
|
28
|
+
playbook """
|
|
29
|
+
Try to print a message (should fail due to missing execute permission).
|
|
30
|
+
|
|
31
|
+
Show: "❌ This should not appear"
|
|
32
|
+
"""
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
run TestAgent.test({})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Simple test for registry operations
|
|
2
|
+
package "test.simple"
|
|
3
|
+
|
|
4
|
+
role Worker { can execute }
|
|
5
|
+
|
|
6
|
+
Agent SimpleWorker : Worker {
|
|
7
|
+
llm default = { provider: "openai", model: "gpt-4o-mini" }
|
|
8
|
+
|
|
9
|
+
on saveUser(args: Json) {
|
|
10
|
+
playbook """
|
|
11
|
+
Save a user to the registry with key "user:${args.id}" and value:
|
|
12
|
+
{
|
|
13
|
+
"name": "${args.name}",
|
|
14
|
+
"age": ${args.age},
|
|
15
|
+
"createdAt": "${Date.now()}"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Return: { "success": true, "message": "User saved" }
|
|
19
|
+
"""
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
on getUser(args: Json) {
|
|
23
|
+
playbook """
|
|
24
|
+
Get the user from registry with key "user:${args.id}".
|
|
25
|
+
If found, return: { "found": true, "user": [the complete user object] }
|
|
26
|
+
If not found, return: { "found": false }
|
|
27
|
+
"""
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
run SimpleWorker.saveUser({ id: "test1", name: "TestUser", age: 25 })
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package "test.typescript.import"
|
|
2
|
+
|
|
3
|
+
// Import TypeScript module
|
|
4
|
+
import "./utils/math-helpers.ts"
|
|
5
|
+
|
|
6
|
+
role Worker { can work }
|
|
7
|
+
|
|
8
|
+
Agent MathAgent : Worker {
|
|
9
|
+
on calculate(args: Json) {
|
|
10
|
+
console.log("=".repeat(60))
|
|
11
|
+
console.log("Testing TypeScript Import")
|
|
12
|
+
console.log("=".repeat(60))
|
|
13
|
+
|
|
14
|
+
// Test simple functions
|
|
15
|
+
const sum = utils_math_helpers.add(5, 3)
|
|
16
|
+
console.log("\nTest 1: add(5, 3) =", sum)
|
|
17
|
+
|
|
18
|
+
const product = utils_math_helpers.multiply(4, 7)
|
|
19
|
+
console.log("Test 2: multiply(4, 7) =", product)
|
|
20
|
+
|
|
21
|
+
const fib10 = utils_math_helpers.fibonacci(10)
|
|
22
|
+
console.log("Test 3: fibonacci(10) =", fib10)
|
|
23
|
+
|
|
24
|
+
const is17Prime = utils_math_helpers.isPrime(17)
|
|
25
|
+
console.log("Test 4: isPrime(17) =", is17Prime)
|
|
26
|
+
|
|
27
|
+
// Test constants
|
|
28
|
+
console.log("\nTest 5: PI =", utils_math_helpers.PI)
|
|
29
|
+
console.log("Test 6: E =", utils_math_helpers.E)
|
|
30
|
+
|
|
31
|
+
// Test class instantiation
|
|
32
|
+
const calc = new utils_math_helpers.Calculator()
|
|
33
|
+
const r1 = calc.add(10, 20)
|
|
34
|
+
const r2 = calc.subtract(50, 15)
|
|
35
|
+
console.log("\nTest 7: Calculator.add(10, 20) =", r1)
|
|
36
|
+
console.log("Test 8: Calculator.subtract(50, 15) =", r2)
|
|
37
|
+
|
|
38
|
+
const history = calc.getHistory()
|
|
39
|
+
console.log("Test 9: Calculator history:", JSON.stringify(history))
|
|
40
|
+
|
|
41
|
+
console.log("\n" + "=".repeat(60))
|
|
42
|
+
console.log("All tests completed!")
|
|
43
|
+
console.log("=".repeat(60))
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
tests: {
|
|
48
|
+
add: sum,
|
|
49
|
+
multiply: product,
|
|
50
|
+
fibonacci: fib10,
|
|
51
|
+
isPrime: is17Prime,
|
|
52
|
+
pi: utils_math_helpers.PI,
|
|
53
|
+
e: utils_math_helpers.E,
|
|
54
|
+
calculator: {
|
|
55
|
+
add: r1,
|
|
56
|
+
subtract: r2,
|
|
57
|
+
history: history
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
run MathAgent.calculate({})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package "test.uses.team"
|
|
2
|
+
|
|
3
|
+
role Worker { can work }
|
|
4
|
+
|
|
5
|
+
Agent TestAgent : Worker {
|
|
6
|
+
on work(args: Json) {
|
|
7
|
+
return { result: "working" }
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
Team TestTeam {
|
|
12
|
+
worker = TestAgent
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Agent MainAgent : Worker {
|
|
16
|
+
// Esta sintaxis SÍ debe funcionar
|
|
17
|
+
uses Team TestTeam
|
|
18
|
+
|
|
19
|
+
on start(args: Json) {
|
|
20
|
+
const result = await send peers.event("work").role(Worker).any()({}) timeout 5s
|
|
21
|
+
return result
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
run MainAgent.start({})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package "test.uses.team"
|
|
2
|
+
|
|
3
|
+
role Worker { can execute }
|
|
4
|
+
role Manager { can manage }
|
|
5
|
+
|
|
6
|
+
Agent SimpleWorker : Worker {
|
|
7
|
+
on doWork(args: Json) {
|
|
8
|
+
playbook """
|
|
9
|
+
Do some work: ${args.task}
|
|
10
|
+
Return JSON: { "result": "work done" }
|
|
11
|
+
"""
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
Team SimpleTeam {
|
|
16
|
+
worker = SimpleWorker
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
Agent TeamUser : Manager {
|
|
20
|
+
uses Team SimpleTeam
|
|
21
|
+
|
|
22
|
+
on requestWork(args: Json) {
|
|
23
|
+
playbook """
|
|
24
|
+
Request work from team: ${args.request}
|
|
25
|
+
Return JSON with actions:
|
|
26
|
+
{ "actions": [{ "intent": "do work", "data": { "task": "${args.request}" } }] }
|
|
27
|
+
"""
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
run TeamUser.requestWork({ request: "process data" })
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Calculator module using Jest
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Calculator, factorial, isPrime } from './calculator';
|
|
6
|
+
|
|
7
|
+
describe('Calculator', () => {
|
|
8
|
+
let calc: Calculator;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
calc = new Calculator();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('add', () => {
|
|
15
|
+
test('should add two positive numbers', () => {
|
|
16
|
+
expect(calc.add(2, 3)).toBe(5);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should add negative numbers', () => {
|
|
20
|
+
expect(calc.add(-5, -3)).toBe(-8);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('should add zero', () => {
|
|
24
|
+
expect(calc.add(5, 0)).toBe(5);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('subtract', () => {
|
|
29
|
+
test('should subtract two numbers', () => {
|
|
30
|
+
expect(calc.subtract(10, 4)).toBe(6);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should handle negative results', () => {
|
|
34
|
+
expect(calc.subtract(5, 10)).toBe(-5);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('multiply', () => {
|
|
39
|
+
test('should multiply two numbers', () => {
|
|
40
|
+
expect(calc.multiply(4, 5)).toBe(20);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should return zero when multiplying by zero', () => {
|
|
44
|
+
expect(calc.multiply(100, 0)).toBe(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should handle negative numbers', () => {
|
|
48
|
+
expect(calc.multiply(-3, 4)).toBe(-12);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('divide', () => {
|
|
53
|
+
test('should divide two numbers', () => {
|
|
54
|
+
expect(calc.divide(10, 2)).toBe(5);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('should handle decimal results', () => {
|
|
58
|
+
expect(calc.divide(7, 2)).toBe(3.5);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should throw error on division by zero', () => {
|
|
62
|
+
expect(() => calc.divide(10, 0)).toThrow('Division by zero');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('power', () => {
|
|
67
|
+
test('should calculate power correctly', () => {
|
|
68
|
+
expect(calc.power(2, 3)).toBe(8);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('should handle exponent of zero', () => {
|
|
72
|
+
expect(calc.power(5, 0)).toBe(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('should handle negative exponents', () => {
|
|
76
|
+
expect(calc.power(2, -2)).toBe(0.25);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('percentage', () => {
|
|
81
|
+
test('should calculate percentage correctly', () => {
|
|
82
|
+
expect(calc.percentage(100, 10)).toBe(10);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('should handle decimal percentages', () => {
|
|
86
|
+
expect(calc.percentage(200, 2.5)).toBe(5);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('factorial', () => {
|
|
92
|
+
test('should calculate factorial of 0', () => {
|
|
93
|
+
expect(factorial(0)).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('should calculate factorial of 1', () => {
|
|
97
|
+
expect(factorial(1)).toBe(1);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should calculate factorial of 5', () => {
|
|
101
|
+
expect(factorial(5)).toBe(120);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('should calculate factorial of 10', () => {
|
|
105
|
+
expect(factorial(10)).toBe(3628800);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('should throw error for negative numbers', () => {
|
|
109
|
+
expect(() => factorial(-1)).toThrow('Factorial is not defined for negative numbers');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('isPrime', () => {
|
|
114
|
+
test('should return false for numbers less than 2', () => {
|
|
115
|
+
expect(isPrime(0)).toBe(false);
|
|
116
|
+
expect(isPrime(1)).toBe(false);
|
|
117
|
+
expect(isPrime(-5)).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should return true for prime numbers', () => {
|
|
121
|
+
expect(isPrime(2)).toBe(true);
|
|
122
|
+
expect(isPrime(3)).toBe(true);
|
|
123
|
+
expect(isPrime(5)).toBe(true);
|
|
124
|
+
expect(isPrime(7)).toBe(true);
|
|
125
|
+
expect(isPrime(11)).toBe(true);
|
|
126
|
+
expect(isPrime(17)).toBe(true);
|
|
127
|
+
expect(isPrime(19)).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('should return false for composite numbers', () => {
|
|
131
|
+
expect(isPrime(4)).toBe(false);
|
|
132
|
+
expect(isPrime(6)).toBe(false);
|
|
133
|
+
expect(isPrime(8)).toBe(false);
|
|
134
|
+
expect(isPrime(9)).toBe(false);
|
|
135
|
+
expect(isPrime(10)).toBe(false);
|
|
136
|
+
expect(isPrime(15)).toBe(false);
|
|
137
|
+
expect(isPrime(21)).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should handle large primes', () => {
|
|
141
|
+
expect(isPrime(97)).toBe(true);
|
|
142
|
+
expect(isPrime(100)).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculator module to demonstrate unit testing in Koi
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class Calculator {
|
|
6
|
+
add(a: number, b: number): number {
|
|
7
|
+
return a + b;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
subtract(a: number, b: number): number {
|
|
11
|
+
return a - b;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
multiply(a: number, b: number): number {
|
|
15
|
+
return a * b;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
divide(a: number, b: number): number {
|
|
19
|
+
if (b === 0) {
|
|
20
|
+
throw new Error('Division by zero');
|
|
21
|
+
}
|
|
22
|
+
return a / b;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
power(base: number, exponent: number): number {
|
|
26
|
+
return Math.pow(base, exponent);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
percentage(value: number, percent: number): number {
|
|
30
|
+
return (value * percent) / 100;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function factorial(n: number): number {
|
|
35
|
+
if (n < 0) {
|
|
36
|
+
throw new Error('Factorial is not defined for negative numbers');
|
|
37
|
+
}
|
|
38
|
+
if (n === 0 || n === 1) {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
return n * factorial(n - 1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isPrime(n: number): boolean {
|
|
45
|
+
if (n <= 1) return false;
|
|
46
|
+
if (n <= 3) return true;
|
|
47
|
+
if (n % 2 === 0 || n % 3 === 0) return false;
|
|
48
|
+
|
|
49
|
+
for (let i = 5; i * i <= n; i += 6) {
|
|
50
|
+
if (n % i === 0 || n % (i + 2) === 0) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math helper utilities (TypeScript module)
|
|
3
|
+
*/
|
|
4
|
+
export function add(a, b) {
|
|
5
|
+
return a + b;
|
|
6
|
+
}
|
|
7
|
+
export function multiply(a, b) {
|
|
8
|
+
return a * b;
|
|
9
|
+
}
|
|
10
|
+
export function fibonacci(n) {
|
|
11
|
+
if (n <= 1)
|
|
12
|
+
return n;
|
|
13
|
+
return fibonacci(n - 1) + fibonacci(n - 2);
|
|
14
|
+
}
|
|
15
|
+
export function isPrime(n) {
|
|
16
|
+
if (n <= 1)
|
|
17
|
+
return false;
|
|
18
|
+
if (n <= 3)
|
|
19
|
+
return true;
|
|
20
|
+
if (n % 2 === 0 || n % 3 === 0)
|
|
21
|
+
return false;
|
|
22
|
+
for (let i = 5; i * i <= n; i += 6) {
|
|
23
|
+
if (n % i === 0 || n % (i + 2) === 0)
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
export class Calculator {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.history = [];
|
|
31
|
+
}
|
|
32
|
+
add(a, b) {
|
|
33
|
+
const result = a + b;
|
|
34
|
+
this.history.push(`${a} + ${b} = ${result}`);
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
subtract(a, b) {
|
|
38
|
+
const result = a - b;
|
|
39
|
+
this.history.push(`${a} - ${b} = ${result}`);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
getHistory() {
|
|
43
|
+
return [...this.history];
|
|
44
|
+
}
|
|
45
|
+
clearHistory() {
|
|
46
|
+
this.history = [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export const PI = 3.141592653589793;
|
|
50
|
+
export const E = 2.718281828459045;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math helper utilities (TypeScript module)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function add(a: number, b: number): number {
|
|
6
|
+
return a + b;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function multiply(a: number, b: number): number {
|
|
10
|
+
return a * b;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function fibonacci(n: number): number {
|
|
14
|
+
if (n <= 1) return n;
|
|
15
|
+
return fibonacci(n - 1) + fibonacci(n - 2);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function isPrime(n: number): boolean {
|
|
19
|
+
if (n <= 1) return false;
|
|
20
|
+
if (n <= 3) return true;
|
|
21
|
+
if (n % 2 === 0 || n % 3 === 0) return false;
|
|
22
|
+
|
|
23
|
+
for (let i = 5; i * i <= n; i += 6) {
|
|
24
|
+
if (n % i === 0 || n % (i + 2) === 0) return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class Calculator {
|
|
31
|
+
private history: string[] = [];
|
|
32
|
+
|
|
33
|
+
add(a: number, b: number): number {
|
|
34
|
+
const result = a + b;
|
|
35
|
+
this.history.push(`${a} + ${b} = ${result}`);
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
subtract(a: number, b: number): number {
|
|
40
|
+
const result = a - b;
|
|
41
|
+
this.history.push(`${a} - ${b} = ${result}`);
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getHistory(): string[] {
|
|
46
|
+
return [...this.history];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
clearHistory(): void {
|
|
50
|
+
this.history = [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const PI = 3.141592653589793;
|
|
55
|
+
export const E = 2.718281828459045;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Web Delegation Demo
|
|
3
|
+
// Demonstrates delegation with indentation and loop detection
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "demo.koi.delegation"
|
|
7
|
+
|
|
8
|
+
role Worker { can execute }
|
|
9
|
+
role Assistant { can help }
|
|
10
|
+
|
|
11
|
+
// ============================================================
|
|
12
|
+
// Skills - JavaScript functions agents can use
|
|
13
|
+
// ============================================================
|
|
14
|
+
|
|
15
|
+
Skill WebFetching {
|
|
16
|
+
affordance """
|
|
17
|
+
Fetch content from web URLs
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
export async function fetchUrl(args: any): Promise<any> {
|
|
21
|
+
const url = args.url;
|
|
22
|
+
console.log("[fetchUrl] Fetching: " + url);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(url, {
|
|
26
|
+
headers: {
|
|
27
|
+
"User-Agent": "Mozilla/5.0 (compatible; KoiBot/1.0)"
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
return {
|
|
33
|
+
url: url,
|
|
34
|
+
content: "Failed to fetch: " + response.status + " " + response.statusText,
|
|
35
|
+
status: "error",
|
|
36
|
+
error: true
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const html = await response.text();
|
|
41
|
+
return {
|
|
42
|
+
url: url,
|
|
43
|
+
content: html,
|
|
44
|
+
status: "success",
|
|
45
|
+
length: html.length
|
|
46
|
+
};
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("[fetchUrl] Error:", error.message);
|
|
49
|
+
return {
|
|
50
|
+
url: url,
|
|
51
|
+
content: "Error fetching page: " + error.message,
|
|
52
|
+
status: "error",
|
|
53
|
+
error: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================
|
|
60
|
+
// Worker Agents with Specialized Capabilities
|
|
61
|
+
// ============================================================
|
|
62
|
+
|
|
63
|
+
Agent WebFetcher : Worker {
|
|
64
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.2, max_tokens: 500 }
|
|
65
|
+
uses Skill WebFetching
|
|
66
|
+
|
|
67
|
+
on fetchWebPage(args: Json) {
|
|
68
|
+
playbook """
|
|
69
|
+
Download the web page from this URL: ${JSON.stringify(args.url)}
|
|
70
|
+
|
|
71
|
+
Use the available fetchUrl tool to download the content, then return the result.
|
|
72
|
+
"""
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Agent ContentSummarizer : Worker {
|
|
77
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, max_tokens: 300 }
|
|
78
|
+
|
|
79
|
+
on summarize(args: Json) {
|
|
80
|
+
playbook """
|
|
81
|
+
Summarize the following content in 1-2 sentences:
|
|
82
|
+
${JSON.stringify(args.content || args.text)}
|
|
83
|
+
|
|
84
|
+
Return your summary as JSON with a "summary" field containing the text.
|
|
85
|
+
"""
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Agent WordCounter : Worker {
|
|
90
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
|
|
91
|
+
|
|
92
|
+
on count(args: Json) {
|
|
93
|
+
playbook """
|
|
94
|
+
Count the number of words in this text: ${JSON.stringify(args.text || args.summary)}
|
|
95
|
+
|
|
96
|
+
Return JSON with "wordCount" field containing the number of words.
|
|
97
|
+
"""
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================================
|
|
102
|
+
// Orchestrator Agent
|
|
103
|
+
// ============================================================
|
|
104
|
+
|
|
105
|
+
Agent TaskOrchestrator : Assistant {
|
|
106
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, max_tokens: 800 }
|
|
107
|
+
|
|
108
|
+
//no cambies este agente!!! Es el que se encarga de delegar la tarea. No cambies su playbook!!!!
|
|
109
|
+
on processWebTask(args: Json) {
|
|
110
|
+
playbook """
|
|
111
|
+
User request: ${args.request}
|
|
112
|
+
|
|
113
|
+
You MUST execute the user request and return the result as JSON. Print also the plan you followed to execute the request.
|
|
114
|
+
"""
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================
|
|
119
|
+
// Team
|
|
120
|
+
// ============================================================
|
|
121
|
+
|
|
122
|
+
Team WorkerTeam {
|
|
123
|
+
orchestrator = TaskOrchestrator
|
|
124
|
+
webFetcher = WebFetcher
|
|
125
|
+
summarizer = ContentSummarizer
|
|
126
|
+
counter = WordCounter
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ============================================================
|
|
130
|
+
// Demo Runner
|
|
131
|
+
// ============================================================
|
|
132
|
+
|
|
133
|
+
Agent DemoRunner : Assistant {
|
|
134
|
+
uses Team WorkerTeam
|
|
135
|
+
|
|
136
|
+
on runDemo(args: Json) {
|
|
137
|
+
console.log("╔════════════════════════════════════════════╗")
|
|
138
|
+
console.log("║ Web Delegation Demo ║")
|
|
139
|
+
console.log("║ Indented Delegation + Loop Detection ║")
|
|
140
|
+
console.log("╚════════════════════════════════════════════╝\n")
|
|
141
|
+
|
|
142
|
+
// ============================================================
|
|
143
|
+
// Test 1: Web → Summarize → Count (requires delegation)
|
|
144
|
+
// ============================================================
|
|
145
|
+
console.log("📋 Test 1: Fetch Web → Summarize → Count Words")
|
|
146
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
147
|
+
console.log('Request: "Fetch web content, summarize it, then count the words in the summary"\n')
|
|
148
|
+
|
|
149
|
+
const test1 = await send peers.event("processWebTask").role(Assistant).any()({
|
|
150
|
+
request: "Fetch web content, summarize it, then count the words in the summary",
|
|
151
|
+
url: "https://soloprogramadores.com",
|
|
152
|
+
topic: "artificial intelligence"
|
|
153
|
+
}) timeout 60s
|
|
154
|
+
|
|
155
|
+
console.log("✅ Final Result:")
|
|
156
|
+
console.log(JSON.stringify(test1, null, 2))
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
tests_completed: 1
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
run DemoRunner.runDemo({})
|