@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,162 @@
1
+ import { mcpClient } from './mcp-client.js';
2
+ import { cliLogger } from './cli-logger.js';
3
+
4
+ export class Team {
5
+ constructor(name, members = {}) {
6
+ this.name = name;
7
+ this.members = members;
8
+ this._mcpResolved = new Map(); // Cache for resolved MCP addresses
9
+
10
+ // Automatically set this team as peers for all agent members
11
+ for (const memberName in members) {
12
+ const member = members[memberName];
13
+ // Check if it's an Agent instance (has handle method)
14
+ // Check if peers is not set or is the no-team proxy
15
+ const hasNoTeam = !member.peers || member.peers.__isNoTeamProxy;
16
+
17
+ if (member && typeof member.handle === 'function' && hasNoTeam) {
18
+ member.peers = this;
19
+ }
20
+ }
21
+ }
22
+
23
+ get(memberName) {
24
+ return this.members[memberName];
25
+ }
26
+
27
+ event(eventName) {
28
+ return new TeamEventQuery(this, eventName);
29
+ }
30
+
31
+ toString() {
32
+ return `Team(${this.name})`;
33
+ }
34
+ }
35
+
36
+ class TeamEventQuery {
37
+ constructor(team, event) {
38
+ this.team = team;
39
+ this.eventName = event;
40
+ this.roleFilter = null;
41
+ this.selectionMode = null;
42
+ }
43
+
44
+ role(roleObj) {
45
+ this.roleFilter = roleObj;
46
+ return this;
47
+ }
48
+
49
+ any() {
50
+ this.selectionMode = 'any';
51
+ return this;
52
+ }
53
+
54
+ all() {
55
+ this.selectionMode = 'all';
56
+ return this;
57
+ }
58
+
59
+ isMCPAddress(value) {
60
+ if (typeof value === 'string' && value.startsWith('mcp://')) {
61
+ return true;
62
+ }
63
+ if (value && value.type === 'MCPAddress') {
64
+ return true;
65
+ }
66
+ return false;
67
+ }
68
+
69
+ async execute(args = {}) {
70
+ // Find matching agents
71
+ const candidates = [];
72
+
73
+ for (const [name, agentOrAddress] of Object.entries(this.team.members)) {
74
+ let agent = agentOrAddress;
75
+
76
+ // If it's an MCP address, resolve it
77
+ if (this.isMCPAddress(agentOrAddress)) {
78
+ const address = typeof agentOrAddress === 'string'
79
+ ? agentOrAddress
80
+ : agentOrAddress.address;
81
+
82
+ cliLogger.progress(`[Team] Resolving MCP: ${address}...`);
83
+
84
+ // Check cache
85
+ if (this.team._mcpResolved.has(address)) {
86
+ agent = this.team._mcpResolved.get(address);
87
+ cliLogger.clear();
88
+ } else {
89
+ // Resolve the MCP address
90
+ try {
91
+ agent = await mcpClient.resolve(address);
92
+ this.team._mcpResolved.set(address, agent);
93
+ cliLogger.clear();
94
+ } catch (error) {
95
+ cliLogger.error(`[Team] Failed to resolve ${address}: ${error.message}`);
96
+ continue;
97
+ }
98
+ }
99
+ }
100
+
101
+ // Check role filter
102
+ if (this.roleFilter && agent.role !== this.roleFilter) {
103
+ continue;
104
+ }
105
+
106
+ // Check if agent has handler for this event
107
+ if (agent.handlers && agent.handlers[this.eventName]) {
108
+ candidates.push({ name, agent });
109
+ } else if (!agent.handlers && typeof agent.handle === 'function') {
110
+ // MCP resources (not regular agents) have a generic handle method
111
+ candidates.push({ name, agent });
112
+ } else if (!agent.handlers && typeof agent.send === 'function') {
113
+ // MCP resources may also have a send method
114
+ candidates.push({ name, agent });
115
+ }
116
+ }
117
+
118
+ if (candidates.length === 0) {
119
+ const roleInfo = this.roleFilter ? ` with role "${this.roleFilter.name}"` : '';
120
+ throw new Error(`NO_AGENT_HANDLER:${this.eventName}:${roleInfo}:${this.team.name}`);
121
+ }
122
+
123
+ // Execute based on selection mode
124
+ if (this.selectionMode === 'any') {
125
+ const selected = candidates[0];
126
+ const agentName = selected.agent.name || selected.name;
127
+
128
+ // Show delegation
129
+ cliLogger.progress(` → [${agentName}] ${this.eventName}...`);
130
+
131
+ // Handle both regular agents and MCP resources
132
+ let result;
133
+ if (selected.agent.handle) {
134
+ // Don't mark as delegation - let the agent decide based on its own context
135
+ result = await selected.agent.handle(this.eventName, args, false);
136
+ } else if (selected.agent.send) {
137
+ result = await selected.agent.send(this.eventName, args);
138
+ } else {
139
+ throw new Error(`Agent ${selected.name} cannot handle event ${this.eventName}`);
140
+ }
141
+ cliLogger.clear();
142
+ return result;
143
+ } else if (this.selectionMode === 'all') {
144
+ const results = [];
145
+ for (const { name, agent } of candidates) {
146
+ const agentName = agent.name || name;
147
+ cliLogger.progress(` → [${agentName}] ${this.eventName}...`);
148
+
149
+ if (agent.handle) {
150
+ // Don't mark as delegation - let the agent decide based on its own context
151
+ results.push(await agent.handle(this.eventName, args, false));
152
+ } else if (agent.send) {
153
+ results.push(await agent.send(this.eventName, args));
154
+ }
155
+ cliLogger.clear();
156
+ }
157
+ return results;
158
+ }
159
+
160
+ throw new Error(`Selection mode not specified (use .any() or .all())`);
161
+ }
162
+ }