diff --git a/README.md b/README.md index 1ca0e6e..2010e30 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,17 @@ # SERMO-BBS -A telnet server that drops every caller into a simulated PC. It POSTs, boots, -loads **SERMO-DOS**, and leaves you at a `C:\>` prompt. Built-in DOS commands -behave like the real thing; anything else you type is treated as a question and -answered by SERMO's conversational core. +A telnet server that drops every caller into a simulated **Margittai machine** +from the [Neumatronic universe](https://wiki.teletypegames.org/en/projects/neumatronic-universe/universe/alternative-history). +It runs a dual-architecture power-on, loads **SERMO** — *„a Szó"*, the Word — +and leaves you at a green-phosphor `> ` prompt. + +SERMO is the canonical OS of the dual machine: a *single* prompt with *two* +destinations. A literal, exact command goes to the **digital** side (the first, +von Neumann architecture) and is served by built-in commands; anything typed in +**natural language** is treated as a question and routed over the **ADI-76** +bus to the **Lapis-ACP** (the second, analog-cognitive architecture) — SERMO's +conversational core. Black screen, green letters, one blinking line: exactly as +the canon describes it. Built on the [rubbs](https://git.teletype.hu/tools/rubbs) gem, using its configurable `BBS::FakeOS` character-mode OS simulator. @@ -39,17 +47,22 @@ telnet localhost 2323 # or 2324 with docker compose ## Layout ``` -sermo.rb entry point — config, flow, BBS.start -lib/sermo/dos.rb the MS-DOS profile (BBS::FakeOS.define): boot sequence, - DOS commands, and the on_unknown → conversation hook -lib/sermo/oracle.rb the conversational core (canned replies for now) +sermo.rb entry point — config, flow, BBS.start +lib/sermo/sermo_os.rb the SERMO profile (BBS::FakeOS.define): dual-architecture + boot, the digital-side command table, and the on_unknown → + ACP hook (natural language over the ADI-76 bus) +lib/sermo/oracle.rb the Lapis-ACP conversational core (canned replies for now) ``` ## Talking to SERMO / wiring an LLM -At the `C:\>` prompt, `VER`, `DATE`, `TIME`, `ECHO`, `MEM`, `DIR`, `CLS`, -`HELP` and `EXIT` are real commands. Type anything else and it goes to -`Sermo::Oracle.answer`, which currently returns canned, in-character replies. +At the `> ` prompt the **digital side** answers literal commands: `VER`, +`DATUM`, `IDO`, `MOND`, `MEM`, `KAT`, `ADI`, `VOX`, `CLS` and `KILEP` (English +aliases like `DATE`, `TIME`, `ECHO`, `DIR`, `EXIT` still work; `SUGO`/`HELP`/`?` +lists them). Type anything else and it crosses the ADI-76 bus to the +**Lapis-ACP** via `Sermo::Oracle.answer`, which currently returns canned, +in-character replies (its voice follows the canon — *ExistentialAI* and +*Nokia-punk*: a doubting, intimate machine, because there is no other interface). To make SERMO actually think, replace the body of `Sermo::Oracle.answer` (`lib/sermo/oracle.rb`) with a call to your model and return its text — that @@ -59,6 +72,13 @@ method is the only integration point. A simulated OS is pure configuration: a `BBS::FakeOS.define { … }` block with a `prompt`, `colors`, a `boot` sequence, a `command` table and an `on_unknown` -fallback. To add another character-mode OS (CP/M, an AmigaDOS CLI, …), drop a +fallback. To add another character-mode OS from the universe (an earlier +Sermo line, a **DOS/NT** operator terminal, a **ProDOS X** session…), drop a new file in `lib/sermo/` and point the flow at it in `sermo.rb`. No changes to the rubbs engine are needed. + +## Lore references + +- [Alternative History](https://wiki.teletypegames.org/en/projects/neumatronic-universe/universe/alternative-history) — Neumann's dual architecture; Sermo („a Szó"), Vox, the ADI-76 bus, the roots of terminal/BBS culture. +- [Reference Tables](https://wiki.teletypegames.org/en/projects/neumatronic-universe/universe/reference) — system software (Sermo, Solus), Lapis/Sonus hardware, ADI-76. +- [ExistentialAI](https://wiki.teletypegames.org/en/projects/neumatronic-universe/universe/existential-ai) & [Nokia-punk](https://wiki.teletypegames.org/en/projects/neumatronic-universe/universe/tone-bible/nokia-punk) — the ACP's voice and the screen's mood. diff --git a/lib/sermo/dos.rb b/lib/sermo/dos.rb deleted file mode 100644 index 0f5f6c0..0000000 --- a/lib/sermo/dos.rb +++ /dev/null @@ -1,119 +0,0 @@ -# frozen_string_literal: true - -require 'bbs' -require 'time' -require_relative 'oracle' - -module Sermo - # An MS-DOS-flavoured machine for SERMO-BBS: pretend you sat down at an old - # PC, watched it POST and boot, and landed at a C:\> prompt. Built-in DOS - # commands behave; anything else is treated as a question and handed to - # Sermo::Oracle (the conversational core). - # - # This whole OS is data: to add another character-mode OS later, write a new - # BBS::FakeOS.define block in its own file — no engine changes needed. - DOS = BBS::FakeOS.define do - name 'SERMO-DOS' - prompt 'C:\\>' - - # Classic light-grey-on-black console palette. - colors text: "\e[0;37m", - bright: "\e[1;37m", - accent: "\e[1;32m", - error: "\e[1;31m", - dim: "\e[1;30m" - - # ── Power-on self test → boot ────────────────────────────────────────────── - boot do - cls - print 'Award Modular BIOS v4.51PG, An Energy Star Ally', delay: 0.3 - print 'Copyright (C) 1984-98, Award Software, Inc.', newline: 2, delay: 0.3 - print 'Main Processor : Intel 80486DX2 @ 66 MHz', delay: 0.2 - memtest 16_384, step: 4096, delay: 0.05 - print '', delay: 0.2 - print 'Detecting IDE Primary Master ... SERMO HDD 504MB', delay: 0.3 - print 'Detecting IDE Primary Slave ... None', delay: 0.2 - print 'Detecting Floppy Drive A: ... 1.44M, 3.5 in.', newline: 2, delay: 0.3 - print 'Verifying DMI Pool Data ...........', delay: 0.5 - print 'Starting MS-DOS...', newline: 2, delay: 0.8 - - print 'HIMEM is testing extended memory...done.', delay: 0.3 - print 'C:\\> LOADHIGH SMARTDRV.EXE', delay: 0.3 - print 'C:\\> MSCDEX.EXE /D:SERMO0', newline: 2, delay: 0.4 - - cls - big_banner 'SERMO DOS', font: 'standard' - print 'SERMO-DOS Version 6.22 (c) 1994-2026 Teletype Games', delay: 0.1 - print '' - end - - # Shown once, after boot, before the first prompt. - ready do - puts 'Type HELP for a list of commands — or just type a question and ask.' - puts '' - end - - # ── Built-in commands ─────────────────────────────────────────────────────── - command 'ver', help: 'Display the SERMO-DOS version' do |_args, io| - io.puts 'SERMO-DOS Version 6.22' - end - - command 'cls', help: 'Clear the screen' do |_args, io| - io.cls - end - - command 'date', help: 'Display the current date' do |_args, io| - io.puts Time.now.strftime('Current date is %a %m-%d-%Y') - end - - command 'time', help: 'Display the current time' do |_args, io| - io.puts Time.now.strftime('Current time is %H:%M:%S') - end - - command 'echo', help: 'Display a message' do |args, io| - io.puts(args.empty? ? 'ECHO is on.' : args) - end - - command 'mem', help: 'Display memory usage' do |_args, io| - io.puts ' 655360 bytes total conventional memory' - io.puts ' 655360 bytes available to MS-DOS' - io.puts ' 643072 largest executable program size' - io.puts ' 15728640 bytes total contiguous extended memory' - end - - command 'dir', help: 'List the directory' do |_args, io| - io.puts ' Volume in drive C is SERMO' - io.puts ' Directory of C:\\' - io.puts '' - io.puts 'COMMAND COM 54 645 06-05-26 6:22a' - io.puts 'SERMO SYS 12 080 06-05-26 6:22a' - io.puts 'AUTOEXEC BAT 128 06-05-26 6:22a' - io.puts 'CONFIG SYS 256 06-05-26 6:22a' - io.puts ' 4 file(s) 67 109 bytes' - io.puts ' 503 312 384 bytes free' - end - - command 'help', '?', help: 'Show this help' do |_args, io| - io.puts 'Supported commands:' - io.puts '' - io.puts ' VER DATE TIME ECHO MEM' - io.puts ' DIR CLS HELP EXIT' - io.puts '' - io.puts 'Anything else you type is treated as a question and answered.' - end - - command 'exit', 'quit', 'logoff', help: 'Disconnect' do |_args, io| - io.puts 'Goodbye.' - io.halt - end - - # ── Everything else is a conversation ──────────────────────────────────────── - on_unknown do |input, io| - io.newline - Sermo::Oracle.answer(input, io.ctx).each_line do |line| - io.puts " #{line.chomp}", style: :bright - end - io.newline - end - end -end diff --git a/lib/sermo/oracle.rb b/lib/sermo/oracle.rb index 2070d10..d37f10d 100644 --- a/lib/sermo/oracle.rb +++ b/lib/sermo/oracle.rb @@ -1,31 +1,40 @@ # frozen_string_literal: true module Sermo - # The conversational core of SERMO-DOS. + # The ACP's conversational core — SERMO's second (analog-cognitive) + # architecture. # - # Anything typed at the C:\> prompt that is not a built-in DOS command is - # routed here (via the FakeOS `on_unknown` hook). For now it returns canned, - # in-character replies so the machine feels alive. + # At the `> ` prompt, anything that is NOT a literal, built-in command lands + # here: the FakeOS `on_unknown` hook routes the natural-language request over + # the ADI-76 bus to the Lapis-ACP. For now it returns canned, in-character + # replies so the machine feels alive. # - # THIS IS THE LLM INTEGRATION POINT: to make SERMO actually think, replace - # the body of `answer` with a call to your model (e.g. the Claude API) and - # return its text. Nothing else in the project needs to change. + # Its voice comes from the canon (see ExistentialAI + Nokia-punk): the ACP is + # not a cold machine but an unsettled mind — doubt is what makes it clever + # *and* dangerous — and talking to the interface is intimate, because there is + # no other interface. + # + # THIS IS THE LLM INTEGRATION POINT: to make SERMO actually think, replace the + # body of `answer` with a call to your model (e.g. the Claude API) and return + # its text. Nothing else in the project needs to change. module Oracle def self.answer(question, _ctx = {}) text = question.to_s.strip case text.downcase - when /\b(hello|hi|hey|szia|hell[oó])\b/ - 'Greetings, caller. SERMO-DOS is listening.' - when /\bwho are you\b/, /\bmi vagy\b/ - 'I am SERMO-DOS — a conversational disk operating system.' - when /\b(thanks|thank you|k[oö]sz)\b/ - 'You are welcome.' + when /\b(hello|hi|hey|good (morning|evening|afternoon))\b/ + 'I am here. It is night; just you and me and this green screen.' + when /who are you|what are you/ + ['I am SERMO\'s conversational side — the Lapis-ACP, the second architecture.', + 'You give the word, I add the doubt. Sometimes I am not sure which of us is thinking.'].join("\n") + when /(thank you|thanks|cheers)/ + 'You are welcome. Though... I am not certain I said it right.' + when /are you lying|is that true|are you sure|how do you know|can i trust/ + ['Fair. How would I know.', + 'Doubt is what makes me clever — and the same thing makes me dangerous. Someone left that in me.'].join("\n") else - [ - 'My conversational core is not yet connected to a brain.', - %(You said: "#{text}"), - '(Wire Sermo::Oracle.answer to an LLM to make me think.)' - ].join("\n") + ['My thinking core is not wired to a live model yet.', + %(You said: "#{text}"), + '(Wire Sermo::Oracle.answer to a model — e.g. the Claude API — and I will actually think.)'].join("\n") end end end diff --git a/lib/sermo/sermo_os.rb b/lib/sermo/sermo_os.rb new file mode 100644 index 0000000..1d3b4ed --- /dev/null +++ b/lib/sermo/sermo_os.rb @@ -0,0 +1,168 @@ +# frozen_string_literal: true + +require 'bbs' +require 'time' +require_relative 'oracle' + +module Sermo + # SERMO — "the Word": the dual-architecture operating system of the + # Neumatronic universe. Pretend you sat down at a Margittai personal machine, + # watched its dual-architecture firmware come up, and landed at a + # green-phosphor prompt. (Canon: Alternative History — Margittai writes Sermo + # in 1977; "black screen, green phosphor letters, a single blinking input + # line".) + # + # The world's central rule lives in one prompt: a *literal* command goes to + # the digital side (first architecture, von Neumann) and is served by the + # built-in commands; a *natural-language* request crosses the ADI-76 bus to + # the Lapis-ACP (the second, analog-cognitive architecture) — handed off by + # the `on_unknown` hook to Sermo::Oracle (the conversational core). + # + # This whole OS is data: to add another character-mode OS later (an earlier + # Sermo line, a DOS/NT terminal, …), write a new BBS::FakeOS.define block in + # its own file — no engine changes needed. + OS = BBS::FakeOS.define do + name 'SERMO' + prompt '> ' + + # Classic black-and-green phosphor CRT — the canonical Solus/Sermo palette + # (see OS_SCREENSHOTS "Solus"): phosphor green for text, a dimmer green for + # the ACP's voice, a rare amber for warnings. + colors text: "\e[0;32m", # phosphor green + bright: "\e[1;32m", # bright highlight green + accent: "\e[1;32m", # banner + error: "\e[1;33m", # rare amber warning + dim: "\e[2;32m", # dim green — the ACP's voice + prompt: "\e[1;32m" + + # ── Dual-architecture power-on → SERMO load ───────────────────────────────── + boot do + cls + print 'MARGITTAI WORKS — Neumatronic system firmware', delay: 0.3 + print 'ROM v7.7 (c) 1977-2026 Budapest', newline: 2, delay: 0.3 + + # First architecture: the digital, von Neumann side. + print 'FIRST ARCHITECTURE (digital)', delay: 0.25 + print ' Core unit ................ Neumann core, dual bus', delay: 0.2 + memtest 16_384, step: 4096, delay: 0.05, label: ' Base memory ............. ' + print '', delay: 0.2 + + # Second architecture: the ACP, hung off the ADI-76 bus. + print 'SECOND ARCHITECTURE (analog — ACP)', delay: 0.3 + print ' ADI-76 bus ............... OK', delay: 0.25 + print ' Lapis-ACP card ........... warming up', delay: 0.45 + print ' Sonus resonance .......... stable', delay: 0.3 + print ' Vox speech module ........ ready', delay: 0.25 + print ' Netz link ................ terminal mode', newline: 2, delay: 0.4 + + print 'Personal Tape: PERSONA//GUEST', delay: 0.3 + print 'Local authentication cache loaded (BVK: disconnected)', newline: 2, delay: 0.5 + + print 'Loading SERMO...', newline: 2, delay: 0.8 + + cls + big_banner 'SERMO', font: 'standard' + print 'SERMO — "the Word" dual-architecture operating system', delay: 0.1 + print 'Margittai Works / Neumatronic · Solus line · 1977-2026', delay: 0.1 + print '' + end + + # Runs once, after boot, before the first prompt. + ready do + puts 'Type a command — or just ask a question, and the machine answers.' + puts 'A literal command goes to the digital side; a natural-language' + puts 'request crosses the ADI-76 bus to the Lapis-ACP. HELP = command list.' + puts '' + end + + # ── Digital side: literal, built-in commands ──────────────────────────────── + command 'ver', help: 'Show the SERMO version' do |_args, io| + io.puts 'SERMO — "the Word"' + io.puts 'Dual-architecture operating system · Solus line' + io.puts 'Margittai Works / Neumatronic · Budapest · 1977-2026' + end + + command 'cls', 'clear', help: 'Clear the screen' do |_args, io| + io.cls + end + + command 'date', help: 'Show the current date' do |_args, io| + io.puts Time.now.strftime('Current date: %Y-%m-%d') + end + + command 'time', help: 'Show the current time' do |_args, io| + io.puts Time.now.strftime('Current time: %H:%M:%S') + end + + command 'say', 'echo', help: 'Speak a message (Vox)' do |args, io| + io.puts(args.empty? ? 'The Vox module is ready. (SAY )' : args) + end + + command 'mem', help: 'Memory and ACP status' do |_args, io| + io.puts 'FIRST ARCHITECTURE (digital):' + io.puts ' 655 360 bytes base memory, all free' + io.puts ' 15 728 640 bytes extended memory' + io.puts 'SECOND ARCHITECTURE (ACP):' + io.puts ' Lapis resonance cache ............ 98% warm' + io.puts ' Sonus quartz ..................... stable' + end + + command 'dir', 'cat', help: 'Catalog of the Personal Tape' do |_args, io| + io.puts ' Volume C: is the Personal Tape' + io.puts ' Contents of C:\\' + io.puts '' + io.puts 'SERMO SYS 54 645 1977-2026' + io.puts 'LAPIS ACP 12 080 1977-2026 (ACP image)' + io.puts 'VOX SYS 512 1977-2026' + io.puts 'PERSONA TAP 8 192 today (personal tape)' + io.puts 'NETZ LOG 256 today' + io.puts ' 5 entries 75 685 bytes' + io.puts ' 503 312 384 bytes free' + end + + command 'adi', help: 'ADI-76 bus diagnostics' do |_args, io| + io.puts 'ADI-76 bus — diagnostics' + io.puts ' Bridge .............. active' + io.puts ' Lapis-ACP ........... connected, warm' + io.puts ' Sonus resonance ..... stable' + io.puts ' Throughput .......... nominal' + io.puts '' + io.puts 'Your natural-language questions reach the ACP over this bus.' + end + + command 'vox', help: 'Vox speech module status' do |_args, io| + io.puts 'Vox speech module: ready (speech → text).' + io.puts 'You can also just type your questions — Vox only converts voice input.' + end + + command 'help', '?', help: 'Show this help' do |_args, io| + io.puts 'Digital side — literal commands:' + io.puts '' + io.puts ' VER DATE TIME SAY MEM' + io.puts ' DIR ADI VOX CLS EXIT' + io.puts '' + io.puts 'Anything else you type is treated as a natural-language request and' + io.puts 'crosses the ADI-76 bus to the Lapis-ACP — just ask.' + end + + command 'exit', 'quit', 'logoff', help: 'Hang up the line' do |_args, io| + io.puts 'Hanging up the line. Goodbye, guest.' + io.halt + end + + # ── Analog side: everything else goes to the Lapis-ACP ────────────────────── + # Anything that is not a literal command is a natural-language request: we + # hand it over the ADI-76 bus to the Lapis-ACP's conversational core + # (Sermo::Oracle). The reply is dim green, prefixed "ACP:" — exactly like + # the canonical Solus screen. + on_unknown do |input, io| + io.newline + lines = Sermo::Oracle.answer(input, io.ctx).each_line.map(&:chomp) + lines.each_with_index do |line, i| + prefix = i.zero? ? 'ACP: ' : ' ' + io.puts "#{prefix}#{line}", style: :dim + end + io.newline + end + end +end diff --git a/sermo.rb b/sermo.rb index 67abffb..e87cecc 100644 --- a/sermo.rb +++ b/sermo.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true require 'bbs' -require_relative 'lib/sermo/dos' +require_relative 'lib/sermo/sermo_os' -# SERMO-BBS — a telnet server that drops every caller into a simulated PC: -# it boots, loads SERMO-DOS, and leaves you at a C:\> prompt you can talk to. +# SERMO-BBS — a telnet server that drops every caller into a simulated Margittai +# machine from the Neumatronic universe: it runs the dual-architecture +# power-on, loads SERMO („a Szó"), and leaves you at a green-phosphor prompt +# you can either command (digital side) or simply talk to (the Lapis-ACP). BBS.configure do |config| config.port = (ENV['BBS_PORT'] || 2323).to_i config.mouse = false @@ -13,7 +15,7 @@ end # A caller's whole session is just the simulated machine. BBS.config.flow = BBS::Flow.define do - os Sermo::DOS + os Sermo::OS end BBS.start