Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- chore: update json-as and remove hack [#857](https://github.com/hypermodeinc/modus/pull/857)
- chore: rename agent lifecycle methods and APIs [#858](https://github.com/hypermodeinc/modus/pull/858)
- feat: enforce WASI reactor mode [#859](https://github.com/hypermodeinc/modus/pull/859)

## 2025-05-22 - Go SDK 0.18.0-alpha.3

Expand Down
2 changes: 1 addition & 1 deletion cli/src/custom/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const ModusHomeDir = process.env.MODUS_HOME || path.join(os.homedir(), ".

export const MinNodeVersion = "22.0.0";
export const MinGoVersion = "1.23.1";
export const MinTinyGoVersion = "0.33.0";
export const MinTinyGoVersion = "0.35.0";

export const GitHubOwner = "hypermodeinc";
export const GitHubRepo = "modus";
Expand Down
16 changes: 16 additions & 0 deletions runtime/plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Plugin struct {
FileName string
Language langsupport.Language
ExecutionPlans map[string]langsupport.ExecutionPlan
StartFunction string
}

func NewPlugin(ctx context.Context, cm wazero.CompiledModule, filename string, md *metadata.Metadata) (*Plugin, error) {
Expand Down Expand Up @@ -83,13 +84,28 @@ func NewPlugin(ctx context.Context, cm wazero.CompiledModule, filename string, m
plans[importName] = plan
}

var startFunction string
if _, found := exports["_initialize"]; found {
// all modules should be reactors, but prior to v0.18, some modules were not.
startFunction = "_initialize"
} else if _, found := exports["_start"]; found {
// this will happen if the module was compiled using TinyGo < 0.35, or Modus AssemblyScript SDK < v0.18.0-alpha.3
startFunction = "_start"
logger.Warn(ctx).Bool("user_visible", true).
Msgf("%s is not correctly configured as a WASI reactor module. Please rebuild the Modus app using the latest version of the Modus SDK.", filename)
} else {
// this path would only occur if the module was not compiled using a Modus SDK
return nil, fmt.Errorf("no WASI startup function found in %s", filename)
}

plugin := &Plugin{
Id: utils.GenerateUUIDv7(),
Module: cm,
Metadata: md,
FileName: filename,
Language: language,
ExecutionPlans: plans,
StartFunction: startFunction,
}

return plugin, nil
Expand Down
6 changes: 3 additions & 3 deletions runtime/wasmhost/wasmhost.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (host *wasmHost) GetModuleInstance(ctx context.Context, plugin *plugins.Plu
span, ctx := utils.NewSentrySpanForCurrentFunc(ctx)
defer span.Finish()

cfg := getModuleConfig(ctx, buffers)
cfg := getModuleConfig(ctx, buffers, plugin)
mod, err := host.runtime.InstantiateModule(ctx, plugin.Module, cfg)
if err != nil {
return nil, fmt.Errorf("failed to instantiate the plugin module: %w", err)
Expand All @@ -127,7 +127,7 @@ func (host *wasmHost) CompileModule(ctx context.Context, bytes []byte) (wazero.C
return cm, nil
}

func getModuleConfig(ctx context.Context, buffers utils.OutputBuffers) wazero.ModuleConfig {
func getModuleConfig(ctx context.Context, buffers utils.OutputBuffers, plugin *plugins.Plugin) wazero.ModuleConfig {

// Get the logger and writers for the plugin's stdout and stderr.
log := logger.Get(ctx).With().Bool("user_visible", true).Logger()
Expand Down Expand Up @@ -155,7 +155,7 @@ func getModuleConfig(ctx context.Context, buffers utils.OutputBuffers) wazero.Mo
// And https://gophers.slack.com/archives/C040AKTNTE0/p1719587772724619?thread_ts=1719522663.531579&cid=C040AKTNTE0
cfg := wazero.NewModuleConfig().
WithName("").
WithStartFunctions("_initialize", "_start").
WithStartFunctions(plugin.StartFunction).
WithSysWalltime().WithSysNanotime().
WithRandSource(rand.Reader).
WithStdout(wOut).WithStderr(wErr).
Expand Down
2 changes: 1 addition & 1 deletion sdk/assemblyscript/src/plugin.asconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"process=wasi_process",
"Date=wasi_Date"
],
"exportStart": "_start",
"exportStart": "_initialize",
"exportRuntime": true
},
"targets": {
Expand Down
16 changes: 3 additions & 13 deletions sdk/go/tools/modus-go-build/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,16 @@ import (
"github.com/hashicorp/go-version"
)

const minTinyGoVersion = "0.33.0"
const minTinyGoVersion = "0.35.0"

func Compile(config *config.Config) error {

tinygoVersion, err := getCompilerVersion(config)
if err != nil {
return err
}

args := []string{"build"}
args = append(args, "-target", "wasip1")
args = append(args, "-o", filepath.Join(config.OutputDir, config.WasmFileName))

// WASI "reactor mode" (-buildmode=c-shared) is required for TinyGo 0.35.0 and later.
// Otherwise, the _start function runs and immediately exits before any function can execute.
// This also switches the startup function to _initialize instead of _start, so the Modus runtime
// needs to match.
if tinygoVersion.GreaterThanOrEqual(version.Must(version.NewVersion("0.35.0"))) {
args = append(args, "-buildmode", "c-shared")
}
// build a WASI reactor module - not a command module
args = append(args, "-buildmode", "c-shared")

// disable the asyncify scheduler until we better understand how to use it
args = append(args, "-scheduler", "none")
Expand Down
Loading