legionio 1.4.73 → 1.4.82

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +11 -0
  3. data/.github/workflow-templates/eval-gate.yml +118 -0
  4. data/.github/workflows/ci-cd.yml +67 -0
  5. data/.github/workflows/ci.yml +13 -0
  6. data/CHANGELOG.md +87 -0
  7. data/CLAUDE.md +39 -45
  8. data/Dockerfile +25 -8
  9. data/README.md +8 -6
  10. data/deploy/helm/legion/Chart.yaml +6 -0
  11. data/deploy/helm/legion/templates/_helpers.tpl +16 -0
  12. data/deploy/helm/legion/templates/deployment-api.yaml +55 -0
  13. data/deploy/helm/legion/templates/deployment-worker.yaml +46 -0
  14. data/deploy/helm/legion/templates/hpa-worker.yaml +22 -0
  15. data/deploy/helm/legion/templates/pdb.yaml +13 -0
  16. data/deploy/helm/legion/templates/service-api.yaml +15 -0
  17. data/deploy/helm/legion/templates/serviceaccount.yaml +8 -0
  18. data/deploy/helm/legion/values.yaml +69 -0
  19. data/docs/plans/2026-03-19-hooks-expansion-design.md +211 -0
  20. data/legionio.gemspec +1 -1
  21. data/lib/legion/api/catalog.rb +82 -0
  22. data/lib/legion/api/hooks.rb +59 -28
  23. data/lib/legion/api/lex.rb +76 -0
  24. data/lib/legion/api/llm.rb +20 -2
  25. data/lib/legion/api/openapi.rb +89 -0
  26. data/lib/legion/api.rb +37 -8
  27. data/lib/legion/cli/check/privacy_check.rb +86 -0
  28. data/lib/legion/cli/check_command.rb +46 -0
  29. data/lib/legion/cli/eval_command.rb +217 -0
  30. data/lib/legion/cli.rb +10 -1
  31. data/lib/legion/data/local_migrations/20260319000001_create_extension_catalog.rb +15 -0
  32. data/lib/legion/data/local_migrations/20260319000002_create_extension_permissions.rb +17 -0
  33. data/lib/legion/extensions/builders/hooks.rb +5 -1
  34. data/lib/legion/extensions/builders/routes.rb +66 -0
  35. data/lib/legion/extensions/builders/runners.rb +1 -0
  36. data/lib/legion/extensions/catalog.rb +102 -0
  37. data/lib/legion/extensions/core.rb +21 -1
  38. data/lib/legion/extensions/hooks/base.rb +5 -1
  39. data/lib/legion/extensions/permissions.rb +141 -0
  40. data/lib/legion/extensions.rb +8 -0
  41. data/lib/legion/service.rb +23 -0
  42. data/lib/legion/version.rb +1 -1
  43. metadata +28 -53
  44. data/lib/legion/api/auth_kerberos.rb +0 -83
  45. data/lib/legion/api/oauth.rb +0 -39
  46. data/lib/legion/mcp/auth.rb +0 -50
  47. data/lib/legion/mcp/context_compiler.rb +0 -173
  48. data/lib/legion/mcp/embedding_index.rb +0 -113
  49. data/lib/legion/mcp/observer.rb +0 -135
  50. data/lib/legion/mcp/resources/extension_info.rb +0 -67
  51. data/lib/legion/mcp/resources/runner_catalog.rb +0 -63
  52. data/lib/legion/mcp/server.rb +0 -165
  53. data/lib/legion/mcp/tool_governance.rb +0 -77
  54. data/lib/legion/mcp/tools/create_chain.rb +0 -50
  55. data/lib/legion/mcp/tools/create_relationship.rb +0 -51
  56. data/lib/legion/mcp/tools/create_schedule.rb +0 -64
  57. data/lib/legion/mcp/tools/delete_chain.rb +0 -52
  58. data/lib/legion/mcp/tools/delete_relationship.rb +0 -52
  59. data/lib/legion/mcp/tools/delete_schedule.rb +0 -52
  60. data/lib/legion/mcp/tools/delete_task.rb +0 -49
  61. data/lib/legion/mcp/tools/describe_runner.rb +0 -92
  62. data/lib/legion/mcp/tools/disable_extension.rb +0 -50
  63. data/lib/legion/mcp/tools/discover_tools.rb +0 -53
  64. data/lib/legion/mcp/tools/do_action.rb +0 -55
  65. data/lib/legion/mcp/tools/enable_extension.rb +0 -50
  66. data/lib/legion/mcp/tools/get_config.rb +0 -63
  67. data/lib/legion/mcp/tools/get_extension.rb +0 -56
  68. data/lib/legion/mcp/tools/get_status.rb +0 -50
  69. data/lib/legion/mcp/tools/get_task.rb +0 -48
  70. data/lib/legion/mcp/tools/get_task_logs.rb +0 -56
  71. data/lib/legion/mcp/tools/list_chains.rb +0 -48
  72. data/lib/legion/mcp/tools/list_extensions.rb +0 -46
  73. data/lib/legion/mcp/tools/list_relationships.rb +0 -45
  74. data/lib/legion/mcp/tools/list_schedules.rb +0 -51
  75. data/lib/legion/mcp/tools/list_tasks.rb +0 -50
  76. data/lib/legion/mcp/tools/list_workers.rb +0 -53
  77. data/lib/legion/mcp/tools/rbac_assignments.rb +0 -45
  78. data/lib/legion/mcp/tools/rbac_check.rb +0 -45
  79. data/lib/legion/mcp/tools/rbac_grants.rb +0 -41
  80. data/lib/legion/mcp/tools/routing_stats.rb +0 -51
  81. data/lib/legion/mcp/tools/run_task.rb +0 -68
  82. data/lib/legion/mcp/tools/show_worker.rb +0 -48
  83. data/lib/legion/mcp/tools/team_summary.rb +0 -53
  84. data/lib/legion/mcp/tools/update_chain.rb +0 -54
  85. data/lib/legion/mcp/tools/update_relationship.rb +0 -55
  86. data/lib/legion/mcp/tools/update_schedule.rb +0 -65
  87. data/lib/legion/mcp/tools/worker_costs.rb +0 -55
  88. data/lib/legion/mcp/tools/worker_lifecycle.rb +0 -54
  89. data/lib/legion/mcp/usage_filter.rb +0 -86
  90. data/lib/legion/mcp.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7693068b5190222c508dd07a248424e8f9e1eff77e80059cbd8a4fc81e9e6325
4
- data.tar.gz: 43cc0da2efe7d604101ab87426c21b5f8ce677a8c023aeec38e5b91018b515ae
3
+ metadata.gz: be7718191541e7f3c7d122c0df0f74145ef0ed5aa8e4b8a67396932155e4cccf
4
+ data.tar.gz: 5b4f4e5453fe9667b6424433d8d0ec9cfe524b6f9c35be64f7e4b0edeae1c3f3
5
5
  SHA512:
6
- metadata.gz: a2d75a86b1646113be917a9f8ed17f8f7aa8ffb48ff371db78ee62fbfdba1929d85e675f2bf7d6a5469b077f10370f3d044eb6f5049db353d471f5be1c3cbab5
7
- data.tar.gz: 2f00a29a6ddf7be18ee71839b97e1a8aeeb74f2cecd820932447bc574fcfeb595a6adce0d757909bf3e3d68909235fcbb32ad95182192b5471dea0a2f3d39a0e
6
+ metadata.gz: dd10e410a69d1cae47dadbaf02f287857410b44e8f8d305c1cd8b555a44a32d2a6b0a63950a3ee2e54d815ef28a25ee7dc08fd7e78a584ec39698b03385bcac0
7
+ data.tar.gz: ce22d223fd1b67df63878d1e1f4d54273437c01a4359f384dc0658ecf06e1e83481285d2b78afd511ee17fabe75a5cb8342826e5cab6ee63cc49a33ccb3c38e4
data/.dockerignore ADDED
@@ -0,0 +1,11 @@
1
+ .git
2
+ .github
3
+ spec
4
+ docs
5
+ *.md
6
+ .rubocop.yml
7
+ .rspec
8
+ tmp
9
+ log
10
+ .dockerignore
11
+ Dockerfile
@@ -0,0 +1,118 @@
1
+ # .github/workflow-templates/eval-gate.yml
2
+ #
3
+ # Eval gate workflow template for LegionIO CI/CD pipelines.
4
+ # Copy this file to .github/workflows/eval-gate.yml in your repo and adjust the
5
+ # env vars to match your dataset and threshold requirements.
6
+ #
7
+ # Required secrets:
8
+ # LEGIONIO_BOOTSTRAP_CONFIG (base64-encoded bootstrap JSON, or omit for defaults)
9
+ #
10
+ # Usage:
11
+ # - Trigger manually (workflow_dispatch) or on push/PR targeting main
12
+ # - Job exits 0 if avg_score >= threshold, exits 1 and fails the pipeline if below
13
+
14
+ name: Eval Gate
15
+
16
+ on:
17
+ push:
18
+ branches: [main]
19
+ pull_request:
20
+ branches: [main]
21
+ workflow_dispatch:
22
+ inputs:
23
+ dataset:
24
+ description: 'Dataset name to evaluate'
25
+ required: true
26
+ default: 'default'
27
+ threshold:
28
+ description: 'Pass/fail threshold (0.0 - 1.0)'
29
+ required: false
30
+ default: '0.8'
31
+ evaluator:
32
+ description: 'Evaluator name (leave blank for first builtin template)'
33
+ required: false
34
+ default: ''
35
+
36
+ env:
37
+ DATASET: ${{ github.event.inputs.dataset || 'default' }}
38
+ THRESHOLD: ${{ github.event.inputs.threshold || '0.8' }}
39
+ EVALUATOR: ${{ github.event.inputs.evaluator || '' }}
40
+
41
+ jobs:
42
+ eval-gate:
43
+ name: Eval Gate (${{ env.DATASET }} @ ${{ env.THRESHOLD }})
44
+ runs-on: ubuntu-latest
45
+
46
+ steps:
47
+ - name: Checkout
48
+ uses: actions/checkout@v4
49
+
50
+ - name: Set up Ruby
51
+ uses: ruby/setup-ruby@v1
52
+ with:
53
+ ruby-version: '3.4'
54
+ bundler-cache: true
55
+
56
+ - name: Install Legion
57
+ run: gem install legionio --no-document
58
+
59
+ - name: Bootstrap config (optional)
60
+ if: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG != '' }}
61
+ env:
62
+ LEGIONIO_BOOTSTRAP_CONFIG: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG }}
63
+ run: echo "Bootstrap config present"
64
+
65
+ - name: Run eval gate
66
+ id: eval
67
+ env:
68
+ LEGIONIO_BOOTSTRAP_CONFIG: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG }}
69
+ run: |
70
+ EVAL_ARGS="--dataset $DATASET --threshold $THRESHOLD --exit-code --json"
71
+ if [ -n "$EVALUATOR" ]; then
72
+ EVAL_ARGS="$EVAL_ARGS --evaluator $EVALUATOR"
73
+ fi
74
+ legion eval run $EVAL_ARGS | tee eval-report.json
75
+
76
+ - name: Upload eval report
77
+ if: always()
78
+ uses: actions/upload-artifact@v4
79
+ with:
80
+ name: eval-report-${{ github.run_number }}
81
+ path: eval-report.json
82
+ retention-days: 30
83
+
84
+ - name: Annotate PR with eval results
85
+ if: github.event_name == 'pull_request' && always()
86
+ uses: actions/github-script@v7
87
+ with:
88
+ script: |
89
+ const fs = require('fs');
90
+ let report;
91
+ try {
92
+ report = JSON.parse(fs.readFileSync('eval-report.json', 'utf8'));
93
+ } catch (e) {
94
+ console.log('Could not parse eval report:', e.message);
95
+ return;
96
+ }
97
+ const gate = report.passed ? 'PASSED' : 'FAILED';
98
+ const score = (report.avg_score || 0).toFixed(3);
99
+ const thresh = report.threshold || 0;
100
+ const body = [
101
+ `## Eval Gate: ${gate}`,
102
+ '',
103
+ `| Metric | Value |`,
104
+ `|--------|-------|`,
105
+ `| Dataset | \`${report.dataset}\` |`,
106
+ `| Evaluator | \`${report.evaluator}\` |`,
107
+ `| Avg Score | ${score} |`,
108
+ `| Threshold | ${thresh} |`,
109
+ `| Total Rows | ${report.summary?.total ?? 'N/A'} |`,
110
+ `| Passed | ${report.summary?.passed ?? 'N/A'} |`,
111
+ `| Failed | ${report.summary?.failed ?? 'N/A'} |`,
112
+ ].join('\n');
113
+ github.rest.issues.createComment({
114
+ issue_number: context.issue.number,
115
+ owner: context.repo.owner,
116
+ repo: context.repo.repo,
117
+ body: body,
118
+ });
@@ -0,0 +1,67 @@
1
+ name: CI/CD
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+
8
+ jobs:
9
+ test:
10
+ name: Test
11
+ runs-on: ubuntu-latest
12
+ services:
13
+ rabbitmq:
14
+ image: rabbitmq:3.13-management
15
+ ports: ['5672:5672']
16
+ postgres:
17
+ image: postgres:16
18
+ env:
19
+ POSTGRES_PASSWORD: test
20
+ POSTGRES_DB: legion_test
21
+ ports: ['5432:5432']
22
+ options: >-
23
+ --health-cmd pg_isready
24
+ --health-interval 10s
25
+ --health-timeout 5s
26
+ --health-retries 5
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: '3.4'
32
+ bundler-cache: true
33
+ - run: bundle install && bundle exec rspec
34
+ - run: bundle exec rubocop
35
+
36
+ build:
37
+ name: Build Image
38
+ needs: test
39
+ if: github.ref == 'refs/heads/main'
40
+ runs-on: ubuntu-latest
41
+ permissions:
42
+ packages: write
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+ - uses: docker/setup-buildx-action@v3
46
+ - uses: docker/login-action@v3
47
+ with:
48
+ registry: ghcr.io
49
+ username: ${{ github.actor }}
50
+ password: ${{ secrets.GITHUB_TOKEN }}
51
+ - uses: docker/build-push-action@v5
52
+ with:
53
+ context: .
54
+ push: true
55
+ tags: |
56
+ ghcr.io/legionio/legion:${{ github.sha }}
57
+ ghcr.io/legionio/legion:latest
58
+ cache-from: type=gha
59
+ cache-to: type=gha,mode=max
60
+
61
+ helm-lint:
62
+ name: Helm Lint
63
+ runs-on: ubuntu-latest
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+ - uses: azure/setup-helm@v3
67
+ - run: helm lint deploy/helm/legion
@@ -14,3 +14,16 @@ jobs:
14
14
  uses: LegionIO/.github/.github/workflows/release.yml@main
15
15
  secrets:
16
16
  rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
17
+
18
+ trigger-homebrew:
19
+ needs: release
20
+ if: needs.release.outputs.changed == 'true'
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Trigger daemon build
24
+ env:
25
+ GH_TOKEN: ${{ secrets.HOMEBREW_DISPATCH_TOKEN }}
26
+ run: |
27
+ gh api repos/LegionIO/homebrew-tap/dispatches \
28
+ -f event_type=build-daemon \
29
+ -f "client_payload[legionio_version]=${{ needs.release.outputs.version }}"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,92 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.82] - 2026-03-20
4
+
5
+ ### Added
6
+ - `legion check --privacy` command: verifies enterprise privacy mode (flag set, no cloud API keys, external endpoints unreachable)
7
+ - `PrivacyCheck` class with three probes: flag_set, no_cloud_keys, no_external_endpoints
8
+ - `Legion::Service.log_privacy_mode_status` logs enterprise privacy state at startup
9
+
10
+ ## [1.4.81] - 2026-03-20
11
+
12
+ ### Added
13
+ - `legion eval experiments` subcommand: list all experiment runs with status and summary
14
+ - `legion eval promote --experiment NAME --tag TAG` subcommand: tag a prompt version for production via lex-prompt
15
+ - `legion eval compare --run1 NAME --run2 NAME` subcommand: side-by-side diff of two experiment runs
16
+ - `require_prompt!` guard for lex-prompt extension availability
17
+
18
+ ## [1.4.80] - 2026-03-20
19
+
20
+ ### Added
21
+ - `legion eval run` CLI subcommand for CI/CD threshold-based eval gating
22
+ - `--dataset`, `--threshold`, `--evaluator`, `--exit-code` options on `eval run`
23
+ - JSON report output to stdout with per-row scores, summary, and timestamp
24
+ - `.github/workflow-templates/eval-gate.yml` reusable GitHub Actions workflow template
25
+ - PR annotation step in workflow template for inline eval result comments
26
+
27
+ ## [1.4.79] - 2026-03-20
28
+
29
+ ### Added
30
+ - Unified LEX routing layer: auto-expose runner functions as POST endpoints at `/api/lex/{ext}/{runner}/{action}`
31
+ - `Builders::Routes` auto-discovers runner public methods during extension autobuild
32
+ - `Routes::Lex` wildcard handler dispatches through Ingress with JWT + RBAC
33
+ - `GET /api/lex` listing endpoint for route discovery
34
+ - Settings-based configuration at `api.lex_routes` (global enable, per-extension enable, runner/function exclusions)
35
+ - `skip_routes` DSL for runner modules to opt out of auto-route exposure
36
+ - Auto-routes included in OpenAPI spec generation
37
+ - `runner_module` reference stored in builders runner hash for introspection
38
+
39
+ ## [1.4.78] - 2026-03-19
40
+
41
+ ### Added
42
+ - Response headers support in `render_custom_response`: runners can return `response[:headers]` hash for custom HTTP headers
43
+
44
+ ### Removed
45
+ - Legacy `POST /api/hooks/:lex_name/:hook_name` route (superseded by `GET|POST /api/hooks/lex/*` splat routes in v1.4.76)
46
+ - Hardcoded `GET /api/auth/negotiate` Kerberos route (migrated to lex-kerberos hook at `/api/hooks/lex/kerberos/negotiate`)
47
+ - `Routes::AuthKerberos` module and `api/auth_kerberos.rb` file
48
+
49
+ ## [1.4.77] - 2026-03-19
50
+
51
+ ### Added
52
+ - Hardcoded deny list in `Extensions::Permissions` blocking access to `~/.ssh`, `~/.gnupg`, `~/.aws/credentials`
53
+ - Deny list overrides all other permission checks including explicit approvals
54
+
55
+ ## [1.4.76] - 2026-03-19
56
+
57
+ ### Added
58
+ - `Hooks::Base.mount(path)` DSL for extension-derived URL suffixes (e.g., `/callback`)
59
+ - `GET /api/hooks/lex/*` splat route for hook discovery via GET requests
60
+ - `POST /api/hooks/lex/*` splat route with `route_path`-based hook dispatch
61
+ - `Legion::API.find_hook_by_path(path)` for direct route-path lookup in hook registry
62
+ - `route_path` field stored in hook registry entries and returned in `GET /api/hooks` listing
63
+ - Runner-controlled responses: `result[:response]` hash with `:status`, `:content_type`, `:body`
64
+ - `build_payload`, `dispatch_hook`, `render_custom_response` extracted helpers in Routes::Hooks
65
+
66
+ ### Changed
67
+ - `register_hook` now accepts `route_path:` keyword; defaults to `lex_name/hook_name` if omitted
68
+ - `builders/hooks.rb` computes `route_path` from `extension_name/hook_name + mount_path`
69
+ - `extensions/core.rb` passes `route_path:` when calling `Legion::API.register_hook`
70
+ - `GET /api/hooks` listing now includes `route_path` and updated `endpoint` field
71
+ - Removed `Routes::OAuth` (moved OAuth callback to lex-microsoft_teams hook with mount path)
72
+ - `handle_hook_request` refactored into smaller helpers to stay within complexity limits
73
+
74
+ ## [1.4.75] - 2026-03-19
75
+
76
+ ### Added
77
+ - `Legion::Extensions::Catalog` singleton state machine tracking extension lifecycle (registered/loaded/starting/running/stopping/stopped)
78
+ - `Legion::Extensions::Permissions` three-layer file permission model (sandbox, declared paths, auto-approve globs)
79
+ - `GET /api/catalog` and `GET /api/catalog/:name` extension capability manifest endpoints
80
+ - Tier 0 routing in `POST /api/llm/chat` via `Legion::MCP::TierRouter` for LLM-free cached responses
81
+ - Data::Local migrations for extension_catalog and extension_permissions tables
82
+ - Catalog lifecycle wired into extension loader (register/loaded/running/stopping/stopped transitions)
83
+
84
+ ## [1.4.74] - 2026-03-19
85
+
86
+ ### Changed
87
+ - Extracted `Legion::MCP` to dedicated `legion-mcp` gem (v0.1.0)
88
+ - Replaced `mcp` gem dependency with `legion-mcp`
89
+
3
90
  ## [1.4.73] - 2026-03-19
4
91
 
5
92
  ### Added
data/CLAUDE.md CHANGED
@@ -9,7 +9,7 @@ The primary gem for the LegionIO framework. An extensible async job engine for s
9
9
 
10
10
  **GitHub**: https://github.com/LegionIO/LegionIO
11
11
  **Gem**: `legionio`
12
- **Version**: 1.4.70
12
+ **Version**: 1.4.79
13
13
  **License**: Apache-2.0
14
14
  **Docker**: `legionio/legion`
15
15
  **Ruby**: >= 3.4
@@ -46,8 +46,8 @@ Legion.start
46
46
  ├── 5. require legion-cache
47
47
  ├── 6. setup_data (legion-data, MySQL/SQLite + migrations, optional)
48
48
  ├── 7. setup_rbac (legion-rbac, optional)
49
- ├── 8. setup_llm (legion-llm, optional)
50
- ├── 9. setup_gaia (legion-gaia, cognitive layer, optional)
49
+ ├── 8. setup_llm (legion-llm, AI provider setup + routing, optional)
50
+ ├── 9. setup_gaia (legion-gaia, cognitive coordination layer, optional)
51
51
  ├── 10. setup_telemetry (OpenTelemetry, optional)
52
52
  ├── 11. setup_supervision (process supervision)
53
53
  ├── 12. load_extensions (two-phase: require+autobuild all, then hook_all_actors)
@@ -95,9 +95,10 @@ Legion (lib/legion.rb)
95
95
  │ │ └── Nothing # No-op actor
96
96
  │ ├── Builders/ # Build actors and runners from LEX definitions
97
97
  │ │ ├── Actors # Build actors from extension definitions
98
- │ │ ├── Runners # Build runners from extension definitions
98
+ │ │ ├── Runners # Build runners from extension definitions (stores runner_module ref)
99
99
  │ │ ├── Helpers # Builder utilities
100
- │ │ └── Hooks # Webhook hook system builder
100
+ │ │ ├── Hooks # Webhook hook system builder
101
+ │ │ └── Routes # Auto-route builder: introspects runners, registers POST /api/lex/* routes
101
102
  │ ├── Helpers/ # Helper mixins for extensions
102
103
  │ │ ├── Base # Base helper mixin
103
104
  │ │ ├── Core # Core helper mixin
@@ -128,6 +129,7 @@ Legion (lib/legion.rb)
128
129
  │ │ ├── Events # SSE stream (sinatra stream) + ring buffer polling fallback
129
130
  │ │ ├── Transport # Connection status, exchanges, queues, publish
130
131
  │ │ ├── Hooks # List + trigger registered extension hooks
132
+ │ │ ├── Lex # Auto-routes: `POST /api/lex/*` wildcard + `GET /api/lex` listing
131
133
  │ │ ├── Workers # Digital worker lifecycle (`/api/workers/*`) + team routes (`/api/teams/*`)
132
134
  │ │ ├── Coldstart # `POST /api/coldstart/ingest` — trigger lex-coldstart ingest from API
133
135
  │ │ ├── Capacity # Aggregate, forecast, per-worker capacity endpoints
@@ -142,26 +144,13 @@ Legion (lib/legion.rb)
142
144
  │ │ ├── ApiVersion # `/api/v1/` rewrite, Deprecation/Sunset headers
143
145
  │ │ ├── BodyLimit # Request body size limit (1MB max, returns 413)
144
146
  │ │ └── RateLimit # Sliding-window rate limiting with per-IP/agent/tenant tiers
145
- └── hook_registry # Class-level registry: register_hook, find_hook, registered_hooks
146
- # Populated by extensions via Legion::API.register_hook(...)
147
+ ├── hook_registry # Class-level registry: register_hook, find_hook, registered_hooks
148
+ # Populated by extensions via Legion::API.register_hook(...)
149
+ │ └── route_registry # Class-level registry: register_route, find_route_by_path, registered_routes
150
+ │ # Populated by Builders::Routes during autobuild
147
151
 
148
- ├── MCP (mcp gem) # MCP server for AI agent integration
149
- ├── MCP.server # Singleton factory: Legion::MCP.server returns MCP::Server instance
150
- │ ├── Server # MCP::Server builder, tool/resource registration
151
- │ ├── Tools/ # 35 MCP::Tool subclasses (legion.* namespace)
152
- │ │ ├── RunTask # Agentic: dot notation task execution
153
- │ │ ├── DescribeRunner # Agentic: runner/function discovery
154
- │ │ ├── List/Get/Delete Task + GetTaskLogs
155
- │ │ ├── List/Create/Update/Delete Chain
156
- │ │ ├── List/Create/Update/Delete Relationship
157
- │ │ ├── List/Get/Enable/Disable Extension
158
- │ │ ├── List/Create/Update/Delete Schedule
159
- │ │ ├── GetStatus, GetConfig
160
- │ │ ├── ListWorkers, ShowWorker, WorkerLifecycle, WorkerCosts, TeamSummary, RoutingStats
161
- │ │ └── RbacAssignments, RbacCheck, RbacGrants
162
- │ └── Resources/
163
- │ ├── RunnerCatalog # legion://runners - all ext.runner.func paths
164
- │ └── ExtensionInfo # legion://extensions/{name} - extension detail template
152
+ ├── MCP (legion-mcp gem) # Extracted to standalone gem see legion-mcp/CLAUDE.md
153
+ └── (35 tools, 2 resources, TierRouter, PatternStore, ContextGuard, Observer, EmbeddingIndex)
165
154
 
166
155
  ├── DigitalWorker # Digital worker platform (AI-as-labor governance)
167
156
  │ ├── Lifecycle # Worker state machine (active/paused/retired/terminated)
@@ -228,6 +217,10 @@ Legion (lib/legion.rb)
228
217
  ├── Pr # `legion pr` - AI-generated PR title and description via LLM
229
218
  ├── Review # `legion review` - AI code review with severity levels
230
219
  ├── Gaia # `legion gaia` - Gaia status
220
+ ├── Llm # `legion llm` - LLM subsystem status and provider health
221
+ ├── Detect # `legion detect scan` - scan environment and recommend extensions
222
+ ├── Observe # `legion observe stats` - MCP tool usage statistics from Observer
223
+ ├── Tty # `legion tty interactive` - launch rich terminal UI (legion-tty)
231
224
  ├── Graph # `legion graph show` - task relationship graph (mermaid/dot)
232
225
  ├── Trace # `legion trace search` - NL trace search via LLM
233
226
  ├── Dashboard # `legion dashboard` - TUI operational dashboard with auto-refresh
@@ -364,7 +357,7 @@ legion
364
357
 
365
358
  plan # read-only exploration mode (no writes/edits/shell)
366
359
  [--model MODEL] [--provider PROVIDER]
367
- # Slash commands: /save (writes plan to docs/plans/), /help, /quit
360
+ # Slash commands: /save (writes plan to docs/work/planning/), /help, /quit
368
361
 
369
362
  swarm # multi-agent workflow orchestration
370
363
  start NAME # run a workflow from .legion/swarms/NAME.json
@@ -483,11 +476,11 @@ legion
483
476
 
484
477
  ### MCP Design
485
478
 
486
- - Uses `mcp` gem (~> 0.8): `MCP::Server`, `MCP::Tool`, `MCP::Resource`
487
- - Transports: `MCP::Server::Transports::StdioTransport`, `MCP::Server::Transports::StreamableHTTPTransport`
488
- - HTTP transport uses rackup + puma
479
+ Extracted to the `legion-mcp` gem (v0.1.0). See `legion-mcp/CLAUDE.md` for full architecture.
480
+
489
481
  - `Legion::MCP.server` is memoized singleton — call `Legion::MCP.reset!` in tests
490
482
  - Tool naming: `legion.snake_case_name` (dot namespace, not slash)
483
+ - Tier 0 routing: PatternStore + TierRouter + ContextGuard for LLM-free cached responses
491
484
 
492
485
  ## Dependencies
493
486
 
@@ -507,7 +500,7 @@ legion
507
500
  | `oj` (>= 3.16) | Fast JSON (C extension) |
508
501
  | `puma` (>= 6.0) | HTTP server for API |
509
502
  | `rackup` (>= 2.0) | Rack server launcher for MCP HTTP transport |
510
- | `mcp` (~> 0.8) | MCP server SDK |
503
+ | `legion-mcp` | MCP server + Tier 0 routing (extracted gem) |
511
504
  | `reline` (>= 0.5) | Interactive line editing for chat REPL |
512
505
  | `rouge` (>= 4.0) | Syntax highlighting for chat markdown rendering |
513
506
  | `tty-spinner` (~> 0.9) | Spinner animation for CLI loading states |
@@ -539,7 +532,7 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
539
532
  | `lib/legion/extensions.rb` | LEX discovery, loading, actor hooking, shutdown |
540
533
  | `lib/legion/extensions/core.rb` | Extension mixin (requirement flags, autobuild) |
541
534
  | `lib/legion/extensions/actors/` | Actor types: base, every, loop, once, poll, subscription, nothing, defaults |
542
- | `lib/legion/extensions/builders/` | Build actors, runners, helpers, hooks from definitions |
535
+ | `lib/legion/extensions/builders/` | Build actors, runners, helpers, hooks, routes from definitions |
543
536
  | `lib/legion/extensions/helpers/` | Mixins: base, core, cache, data, logger, transport, task, lex |
544
537
  | `lib/legion/extensions/data/` | Extension-level migrator and model |
545
538
  | `lib/legion/extensions/hooks/base.rb` | Webhook hook base class |
@@ -572,16 +565,19 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
572
565
  | `lib/legion/api/settings.rb` | Settings: read/write with redaction + readonly guards |
573
566
  | `lib/legion/api/events.rb` | Events: SSE stream + polling fallback (ring buffer) |
574
567
  | `lib/legion/api/transport.rb` | Transport: status, exchanges, queues, publish |
575
- | `lib/legion/api/hooks.rb` | Hooks: list registered + trigger via Ingress |
568
+ | `lib/legion/api/hooks.rb` | Hooks: list registered + trigger via Ingress; supports custom response headers |
569
+ | `lib/legion/api/lex.rb` | Lex auto-routes: `POST /api/lex/*` wildcard dispatch + `GET /api/lex` listing |
576
570
  | `lib/legion/api/workers.rb` | Workers + Teams: digital worker lifecycle REST endpoints (`/api/workers/*`) and team cost endpoints (`/api/teams/*`) |
577
571
  | `lib/legion/api/coldstart.rb` | Coldstart: `POST /api/coldstart/ingest` — triggers lex-coldstart ingest runner (requires lex-coldstart + lex-memory) |
578
572
  | `lib/legion/api/gaia.rb` | Gaia: system status endpoints |
579
573
  | `lib/legion/api/token.rb` | Token: JWT token issuance endpoint |
580
574
  | `lib/legion/api/openapi.rb` | OpenAPI: `Legion::API::OpenAPI.spec` / `.to_json`; also served at `GET /api/openapi.json` |
581
- | `lib/legion/api/oauth.rb` | OAuth: `GET /api/oauth/microsoft_teams/callback` — receives delegated OAuth redirect and stores tokens |
582
575
  | `lib/legion/api/capacity.rb` | Capacity: aggregate, forecast, and per-worker capacity endpoints |
583
576
  | `lib/legion/api/tenants.rb` | Tenants: listing, provisioning, suspension, quota check |
577
+ | `lib/legion/api/catalog.rb` | Catalog: extension catalog with metadata endpoints |
578
+ | `lib/legion/api/llm.rb` | LLM: provider status and routing configuration endpoints |
584
579
  | `lib/legion/api/audit.rb` | Audit: list, show, count, export audit log entries |
580
+ | `lib/legion/api/auth.rb` | Auth: combined token exchange endpoint (`POST /api/auth/token` — JWKS verify + RBAC claims mapper) |
585
581
  | `lib/legion/api/auth_human.rb` | Auth: human user authentication endpoints |
586
582
  | `lib/legion/api/auth_worker.rb` | Auth: digital worker authentication endpoints |
587
583
  | `lib/legion/api/rbac.rb` | RBAC: role listing, permission grants, access checks |
@@ -604,19 +600,12 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
604
600
  | `lib/legion/tenant_context.rb` | Thread-local tenant context propagation (set, clear, with block) |
605
601
  | `lib/legion/tenants.rb` | Tenant CRUD, suspension, quota enforcement |
606
602
  | `lib/legion/capacity/model.rb` | Workforce capacity calculation (throughput, utilization, forecast, per-worker) |
607
- | **MCP** | |
608
- | `lib/legion/mcp.rb` | Entry point: `Legion::MCP.server` singleton factory, `server_for(token:)` |
609
- | `lib/legion/mcp/auth.rb` | MCP authentication: JWT + API key verification |
610
- | `lib/legion/mcp/tool_governance.rb` | Risk-tier tool filtering and invocation audit |
611
- | `lib/legion/mcp/server.rb` | MCP::Server builder, TOOL_CLASSES array, governance-aware build |
603
+ | **MCP** (extracted to `legion-mcp` gem) | |
612
604
  | `lib/legion/digital_worker.rb` | DigitalWorker module entry point |
613
605
  | `lib/legion/digital_worker/lifecycle.rb` | Worker state machine |
614
606
  | `lib/legion/digital_worker/registry.rb` | In-process worker registry |
615
607
  | `lib/legion/digital_worker/risk_tier.rb` | AIRB risk tier + governance constraints |
616
608
  | `lib/legion/digital_worker/value_metrics.rb` | Token/cost/latency tracking |
617
- | `lib/legion/mcp/tools/` | 35 MCP::Tool subclasses (incl. rbac_assignments, rbac_check, rbac_grants) |
618
- | `lib/legion/mcp/resources/runner_catalog.rb` | `legion://runners` resource |
619
- | `lib/legion/mcp/resources/extension_info.rb` | `legion://extensions/{name}` resource template |
620
609
  | **CLI v2** | |
621
610
  | `lib/legion/cli.rb` | `Legion::CLI::Main` Thor app, global flags, version, start/stop/status/check |
622
611
  | `lib/legion/cli/output.rb` | `Output::Formatter`: color, tables, JSON mode, ANSI stripping |
@@ -676,16 +665,23 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
676
665
  | `lib/legion/cli/version.rb` | CLI version display helper |
677
666
  | `lib/legion/docs/site_generator.rb` | Static documentation site generator |
678
667
  | `lib/legion/cli/memory_command.rb` | `legion memory` subcommands (list, add, forget, search, clear) |
679
- | `lib/legion/cli/plan_command.rb` | `legion plan` — read-only exploration mode with /save to docs/plans/ |
668
+ | `lib/legion/cli/plan_command.rb` | `legion plan` — read-only exploration mode with /save to docs/work/planning/ |
680
669
  | `lib/legion/cli/swarm_command.rb` | `legion swarm` — multi-agent workflow orchestration from `.legion/swarms/` |
681
670
  | `lib/legion/cli/commit_command.rb` | `legion commit` — AI-generated commit messages via LLM |
682
671
  | `lib/legion/cli/pr_command.rb` | `legion pr` — AI-generated PR title + description via LLM |
683
672
  | `lib/legion/cli/review_command.rb` | `legion review` — AI code review with severity levels (CRITICAL/WARNING/SUGGESTION/NOTE) |
684
673
  | `lib/legion/cli/gaia_command.rb` | `legion gaia` subcommands (status) |
674
+ | `lib/legion/cli/llm_command.rb` | `legion llm` subcommands (status) — LLM subsystem status and provider health |
675
+ | `lib/legion/cli/detect_command.rb` | `legion detect scan` — scan environment and recommend extensions |
676
+ | `lib/legion/cli/observe_command.rb` | `legion observe stats` — MCP tool usage statistics from Observer |
677
+ | `lib/legion/cli/tty_command.rb` | `legion tty interactive` — launch rich terminal UI (legion-tty interactive shell) |
678
+ | `lib/legion/cli/interactive.rb` | `Interactive` Thor class — shared CLI module for `legion` binary entry point |
679
+ | `lib/legion/cli/config_import.rb` | `legion config import` — import config from external sources |
685
680
  | `lib/legion/cli/schedule_command.rb` | `legion schedule` subcommands (list, show, add, remove, logs) |
686
681
  | `lib/legion/cli/completion_command.rb` | `legion completion` subcommands (bash, zsh, install) |
687
682
  | `lib/legion/cli/openapi_command.rb` | `legion openapi` subcommands (generate, routes); also `GET /api/openapi.json` endpoint |
688
- | `lib/legion/cli/doctor_command.rb` | `legion doctor` — 10-check environment diagnosis; `Doctor::Result` value object with status/message/prescription/auto_fixable |
683
+ | `lib/legion/cli/doctor_command.rb` | `legion doctor` — 11-check environment diagnosis; `Doctor::Result` value object with status/message/prescription/auto_fixable |
684
+ | `lib/legion/cli/doctor/` | Individual check modules: ruby_version, bundle, config, rabbitmq, database, cache, vault, extensions, pid, permissions, plus result.rb |
689
685
  | `lib/legion/cli/telemetry_command.rb` | `legion telemetry` subcommands (stats, ingest) — session log analytics |
690
686
  | `lib/legion/cli/auth_command.rb` | `legion auth` subcommands (teams) — delegated OAuth browser flow for external services |
691
687
  | `completions/legion.bash` | Bash tab completion script |
@@ -713,8 +709,6 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
713
709
  | `API::Routes::Relationships` | Fully implemented (backed by legion-data migration 013) |
714
710
  | `API::Routes::Chains` | 501 stub - no data model |
715
711
  | `API::Middleware::Auth` | JWT Bearer auth middleware — real token validation and API key (`X-API-Key` header) auth both implemented |
716
- | `MCP::Auth` | JWT + API key authentication for MCP server (HTTP transport) |
717
- | `MCP::ToolGovernance` | Risk-tier tool filtering + audit — disabled by default, opt-in via settings |
718
712
  | `legion-data` chains/relationships models | Not yet implemented |
719
713
 
720
714
  ## Rubocop Notes
@@ -728,7 +722,7 @@ rack-test, rake, rspec, rubocop, rubocop-rspec, simplecov
728
722
 
729
723
  ```bash
730
724
  bundle install
731
- bundle exec rspec # 1433 examples, 0 failures
725
+ bundle exec rspec # 1499 examples, 0 failures
732
726
  bundle exec rubocop # 418 files, 0 offenses
733
727
  ```
734
728
 
data/Dockerfile CHANGED
@@ -1,9 +1,26 @@
1
- FROM ruby:3.4-alpine
2
- LABEL maintainer="Matthew Iverson <matthewdiverson@gmail.com>"
1
+ # Build stage
2
+ FROM ruby:3.4-slim AS builder
3
+ WORKDIR /app
4
+ RUN apt-get update && \
5
+ apt-get install -y --no-install-recommends build-essential libpq-dev git && \
6
+ rm -rf /var/lib/apt/lists/*
7
+ COPY Gemfile Gemfile.lock ./
8
+ RUN bundle config set --local deployment true && \
9
+ bundle config set --local without 'development test' && \
10
+ bundle install --jobs 4 --retry 3
11
+ COPY . .
3
12
 
4
- RUN mkdir /etc/legionio
5
- RUN apk update && apk add build-base postgresql-dev mysql-client mariadb-dev tzdata gcc git
6
-
7
- COPY . ./
8
- RUN gem install legionio tzinfo-data tzinfo --no-document --no-prerelease
9
- CMD ruby --yjit $(which legion)
13
+ # Runtime stage
14
+ FROM ruby:3.4-slim AS runtime
15
+ RUN apt-get update && \
16
+ apt-get install -y --no-install-recommends libpq5 curl && \
17
+ rm -rf /var/lib/apt/lists/* && \
18
+ groupadd -r legion && useradd -r -g legion -d /app -s /sbin/nologin legion
19
+ WORKDIR /app
20
+ COPY --from=builder --chown=legion:legion /app /app
21
+ USER legion
22
+ EXPOSE 4567
23
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
24
+ CMD curl -sf http://localhost:4567/api/health || exit 1
25
+ ENTRYPOINT ["bundle", "exec"]
26
+ CMD ["legion", "start"]
data/README.md CHANGED
@@ -14,7 +14,7 @@ Schedule tasks, chain services into dependency graphs, run them concurrently via
14
14
  ╰──────────────────────────────────────╯
15
15
  ```
16
16
 
17
- **Ruby >= 3.4** | **v1.4.67** | **Apache-2.0** | [@Esity](https://github.com/Esity)
17
+ **Ruby >= 3.4** | **v1.4.78** | **Apache-2.0** | [@Esity](https://github.com/Esity)
18
18
 
19
19
  ---
20
20
 
@@ -461,11 +461,13 @@ legion start
461
461
  ├── 4. Transport (legion-transport — RabbitMQ)
462
462
  ├── 5. Cache (legion-cache — Redis/Memcached)
463
463
  ├── 6. Data (legion-data — database + migrations)
464
- ├── 7. LLM (legion-llmAI provider setup + routing)
465
- ├── 8. Supervision (process supervision)
466
- ├── 9. Extensions (discover + load 280+ LEX gems, filtered by role profile)
467
- ├── 10. Cluster Secret (distribute via Vault or memory)
468
- └── 11. API (Sinatra/Puma on port 4567)
464
+ ├── 7. RBAC (legion-rbacoptional role-based access control)
465
+ ├── 8. LLM (legion-llm — AI provider setup + routing)
466
+ ├── 9. GAIA (legion-gaia cognitive coordination layer)
467
+ ├── 10. Supervision (process supervision)
468
+ ├── 11. Extensions (discover + load 280+ LEX gems, filtered by role profile)
469
+ ├── 12. Cluster Secret (distribute via Vault or memory)
470
+ └── 13. API (Sinatra/Puma on port 4567)
469
471
  ```
470
472
 
471
473
  Each phase registers with `Legion::Readiness`. All phases are individually toggleable.
@@ -0,0 +1,6 @@
1
+ apiVersion: v2
2
+ name: legion
3
+ description: LegionIO async job engine
4
+ version: 0.1.0
5
+ appVersion: "1.4.13"
6
+ type: application
@@ -0,0 +1,16 @@
1
+ {{- define "legion.fullname" -}}
2
+ {{- .Release.Name }}-legion
3
+ {{- end }}
4
+
5
+ {{- define "legion.labels" -}}
6
+ app.kubernetes.io/name: legion
7
+ app.kubernetes.io/instance: {{ .Release.Name }}
8
+ app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
9
+ app.kubernetes.io/managed-by: {{ .Release.Service }}
10
+ helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
11
+ {{- end }}
12
+
13
+ {{- define "legion.selectorLabels" -}}
14
+ app.kubernetes.io/name: legion
15
+ app.kubernetes.io/instance: {{ .Release.Name }}
16
+ {{- end }}