diff --git a/cmd/mavweb/chat.html b/cmd/mavweb/chat.html new file mode 100644 index 0000000..b04f0d4 --- /dev/null +++ b/cmd/mavweb/chat.html @@ -0,0 +1,24 @@ +{{template "shellTop" "chat"}} +

Chat

+
+{{if .Error}}
{{.Error}}
{{end}} +
+{{range .Messages}} +
{{if eq .Role "user"}}you{{else}}maven{{end}}: {{.Text}}
+{{else}} +
+ +
start a conversation
+
+{{end}} +
+
+ + +
+
+ +{{template "shellBottom"}} diff --git a/cmd/mavweb/main.go b/cmd/mavweb/main.go index e6c8c0c..69bcf58 100644 --- a/cmd/mavweb/main.go +++ b/cmd/mavweb/main.go @@ -75,6 +75,15 @@ var morningHTML string //go:embed events.html var eventsHTML string +//go:embed tools.html +var toolsHTML string + +//go:embed routines.html +var routinesHTML string + +//go:embed chat.html +var chatPageHTML string + // shellHTML — the shell partial every page is wrapped in: "shellTop", the // "sidebar" it calls, and "shellBottom". It used to be two Go string constants // with the sidebar assembled by a strings.Builder, which is the one piece of @@ -672,92 +681,6 @@ var toolsTmpl = template.Must(template.New("tools").Funcs(func() template.FuncMa return m }()).Parse(shellHTML + toolsHTML)) -const toolsHTML = `{{template "shellTop" "tools"}} -

Tools

-

enabling requires step-up — assert a passkey first.

-{{if .Msg}}
{{.Msg}}
{{end}} -
-

proposed {{len .Proposed}}

-{{if .Proposed}}

maven drafted these from acts she couldn't run. Fill the command (argv, space-separated) and enable. A row in an mcp: scope came from an MCP server and already knows what it calls — check the command, then enable.

-
-{{range .Proposed}} - - -{{end}}
namescopefrom utteranceenable as
{{.Name}}{{.Scope}}{{.Utterance}}
- - - - - -
-
- - -
-{{else}}
- -
no proposed tools
-
maven will propose tools here when she needs help running an action
-
{{end}} -
-
-

enabled {{len .Enabled}}

-{{if .Enabled}}
-{{range .Enabled}} - -{{end}}
namescopecommand
{{.Name}}{{.Scope}}{{join .Cmd " "}}{{if .Destructive}}destructive{{end}}
- - - -
-{{else}}
- -
no tools enabled
-
enable proposed tools above, or ask maven to configure one
-
{{end}} -
-
-

MCP servers {{len .MCP}}

-{{if .MCP}}

servers she connects OUT to. Their tools appear above as proposals — a configured server is a place she may look, not a capability she has. A stdio target is a process on this box; an http one on a loopback or LAN address is inside the network, so treat its tools accordingly.

-
-{{range .MCP}} - -{{end}}
nametransporttargetstatetools
{{.Name}}{{.Transport}}{{.Target}}{{if .Connected}}connected{{if .Server}} — {{.Server}}{{end}}{{else}}down{{if .Err}} — {{.Err}}{{end}}{{end}}{{.Tools}}
-{{else}}
- -
no MCP servers configured
-
add an mcp.servers block to mavend.json to let her use an external tool server
-
{{end}} -
-{{template "shellBottom"}}` - -// routinesHTML — proposed routine review surface. One row per thing maven -// noticed, in her words, with at most two actions: accept or dismiss. -const routinesHTML = `{{template "shellTop" "routines"}} -

Routines

-{{if .Msg}}
{{.Msg}}
{{end}} -
-

noticed {{len .Proposed}}

-{{if .Proposed}}
-{{range .Proposed}} - - - -{{end}}
maven noticedwhen
{{.Phrase}}{{.Noticed}}
- - -
- - -
-{{else}}
- -
no proposed routines
-
maven will propose routines here when she detects a recurring pattern
-
{{end}} -
-{{template "shellBottom"}}` - var historyTmpl = template.Must(template.New("history").Funcs(shellFuncs()).Parse(shellHTML + historyHTML)) var notificationsTmpl = template.Must(template.New("notifications").Funcs(shellFuncs()).Parse(shellHTML + notificationsHTML)) @@ -770,6 +693,8 @@ var voiceTmpl = template.Must(template.New("voice").Funcs(shellFuncs()).Parse(sh var tasksTmpl = template.Must(template.New("tasks").Funcs(shellFuncs()).Parse(shellHTML + tasksHTML)) +// routinesTmpl — the proposed-routine review surface. One row per thing maven +// noticed, in her words, with at most two actions: accept or dismiss. var routinesTmpl = template.Must(template.New("routines").Funcs(shellFuncs()).Parse(shellHTML + routinesHTML)) var traceTmpl = template.Must(template.New("trace").Funcs(func() template.FuncMap { @@ -1095,9 +1020,10 @@ func fmtTaskDate(t *time.Time) string { return t.Local().Format("02 Jan") } -// routineRow is one line on the page: what maven noticed, in her words, and -// how long ago she noticed it. -type routineRow struct { +// routineView is one line on the page: what maven noticed, in her words, and +// how long ago she noticed it. A view model, not a database row — the template +// never formats an interval or a timestamp itself. +type routineView struct { ID int64 Phrase string Noticed string @@ -1159,23 +1085,23 @@ func handleRoutines(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI, se w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := routinesTmpl.Execute(w, struct { Msg string - Proposed []routineRow - }{msg, routineRows(proposed)}); err != nil { + Proposed []routineView + }{msg, toRoutineViews(proposed)}); err != nil { log.Printf("routines render: %v", err) } } -// routineRows turns the wire rows into display rows. The phrase comes from +// toRoutineViews turns the wire rows into view models. The phrase comes from // pattern.PhraseRoutine so the page says the same thing maven's voice says. -func routineRows(rs []ipc.ProposedRoutine) []routineRow { - out := make([]routineRow, 0, len(rs)) +func toRoutineViews(rs []ipc.ProposedRoutine) []routineView { + out := make([]routineView, 0, len(rs)) for _, r := range rs { p := pattern.ProposedRoutine{Action: r.Action, Object: r.Object, IntervalDays: r.IntervalDays} noticed := "just now" if r.CreatedTs > 0 { noticed = time.Since(time.UnixMilli(r.CreatedTs)).Round(time.Minute).String() + " ago" } - out = append(out, routineRow{ID: r.ID, Phrase: pattern.PhraseRoutine(&p), Noticed: noticed}) + out = append(out, routineView{ID: r.ID, Phrase: pattern.PhraseRoutine(&p), Noticed: noticed}) } return out } @@ -1574,31 +1500,6 @@ func handlePTT(w http.ResponseWriter, r *http.Request, voiceAddr string, session // and the handler redirects back to /chat with the response. var chatTmpl = template.Must(template.New("chat").Funcs(shellFuncs()).Parse(shellHTML + chatPageHTML)) -const chatPageHTML = `{{template "shellTop" "chat"}} -

Chat

-
-{{if .Error}}
{{.Error}}
{{end}} -
-{{range .Messages}} -
{{if eq .Role "user"}}you{{else}}maven{{end}}: {{.Text}}
-{{else}} -
- -
start a conversation
-
-{{end}} -
-
- - -
-
- -{{template "shellBottom"}}` - // handleChatPage renders the chat conversation page. func handleChatPage(w http.ResponseWriter, r *http.Request, core ipc.CoreAPI) { if core == nil { diff --git a/cmd/mavweb/routines.html b/cmd/mavweb/routines.html new file mode 100644 index 0000000..1262624 --- /dev/null +++ b/cmd/mavweb/routines.html @@ -0,0 +1,24 @@ +{{template "shellTop" "routines"}} +

Routines

+{{if .Msg}}
{{.Msg}}
{{end}} +
+

noticed {{len .Proposed}}

+{{if .Proposed}}
+{{range .Proposed}} + + + +{{end}}
maven noticedwhen
{{.Phrase}}{{.Noticed}}
+ + +
+ + +
+{{else}}
+ +
no proposed routines
+
maven will propose routines here when she detects a recurring pattern
+
{{end}} +
+{{template "shellBottom"}} diff --git a/cmd/mavweb/tools.html b/cmd/mavweb/tools.html new file mode 100644 index 0000000..7805d65 --- /dev/null +++ b/cmd/mavweb/tools.html @@ -0,0 +1,58 @@ +{{template "shellTop" "tools"}} +

Tools

+

enabling requires step-up — assert a passkey first.

+{{if .Msg}}
{{.Msg}}
{{end}} +
+

proposed {{len .Proposed}}

+{{if .Proposed}}

maven drafted these from acts she couldn't run. Fill the command (argv, space-separated) and enable. A row in an mcp: scope came from an MCP server and already knows what it calls — check the command, then enable.

+
+{{range .Proposed}} + + +{{end}}
namescopefrom utteranceenable as
{{.Name}}{{.Scope}}{{.Utterance}}
+ + + + + +
+
+ + +
+{{else}}
+ +
no proposed tools
+
maven will propose tools here when she needs help running an action
+
{{end}} +
+
+

enabled {{len .Enabled}}

+{{if .Enabled}}
+{{range .Enabled}} + +{{end}}
namescopecommand
{{.Name}}{{.Scope}}{{join .Cmd " "}}{{if .Destructive}}destructive{{end}}
+ + + +
+{{else}}
+ +
no tools enabled
+
enable proposed tools above, or ask maven to configure one
+
{{end}} +
+
+

MCP servers {{len .MCP}}

+{{if .MCP}}

servers she connects OUT to. Their tools appear above as proposals — a configured server is a place she may look, not a capability she has. A stdio target is a process on this box; an http one on a loopback or LAN address is inside the network, so treat its tools accordingly.

+
+{{range .MCP}} + +{{end}}
nametransporttargetstatetools
{{.Name}}{{.Transport}}{{.Target}}{{if .Connected}}connected{{if .Server}} — {{.Server}}{{end}}{{else}}down{{if .Err}} — {{.Err}}{{end}}{{end}}{{.Tools}}
+{{else}}
+ +
no MCP servers configured
+
add an mcp.servers block to mavend.json to let her use an external tool server
+
{{end}} +
+{{template "shellBottom"}}