pwn 0.5.759 → 0.5.761

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ef7228011e10bd0573cab148f192e8ef963608a002d4e3d833fcd2e380fd973
4
- data.tar.gz: 8f238e16e7d0d521deb4b4abdad45fed2d15fb82d92bcc8448b0877bc23b8783
3
+ metadata.gz: 6748541ed45249f60331d0a819803c7c872527b763806edbcdd601ca30e862dc
4
+ data.tar.gz: e9f1ff039dafe19a3f250df08896bdf2498f6c5550d28dae4c83e2a071560c58
5
5
  SHA512:
6
- metadata.gz: d6eb621a41250c5332e00f941c8b6b7444f796fcb30c009d774d7a0814f3c268809152275f2c63f7f7076450c75bbe662c0248ec6b21abb9730b44b097fdb139
7
- data.tar.gz: 8ea184fdfa2018bc59f00aef8585d190907236a3d71df302c21db3eb025813111cd5a7bb289513c5a7f51a816f1c7e094f5b6c0bf420ff50d9a7a27a11ffbc65
6
+ metadata.gz: 4c07ed5325a0db30c3933f8157e72b65e1f1ff3428e1944fb43f5809390cc647486fd9ed36b76c3986a2c55f75fba0d49c552ab3a1cb4b6dc1dc1e7669e3615f
7
+ data.tar.gz: 880e597ef1c96a3cc197bdf56b317f1e0854d17ffafcf7780c6f97ca198bce7dbcda6c971bdf0fba0d2feea29de7794f6e7af1cf9d17d99471e325e19db9582a
data/Gemfile CHANGED
@@ -49,7 +49,7 @@ gem 'jwt', '3.3.0'
49
49
  gem 'libusb', '0.8.0'
50
50
  gem 'luhn', '3.0.0'
51
51
  gem 'mail', '2.9.1'
52
- gem 'mcp', '1.6.1'
52
+ gem 'mcp', '1.7.0'
53
53
  gem 'meshtastic', '0.0.186'
54
54
  gem 'metasm', '1.0.6'
55
55
  gem 'mongo', '2.26.0'
@@ -22,12 +22,46 @@ ASSISTANT and WARNING), a persistent multiline composer, and an operational
22
22
  sidebar at 100 columns or wider. Narrower screens give the timeline the full width.
23
23
  At 100 columns × 26 rows or larger, the header may carve out a bordered retro-game
24
24
  colored Unicode-block animation on the left: falling tetrominoes with line clears,
25
- a connected snake eating food and growing, Pong with small tracking paddles and a
26
- bouncing ball, or Asteroids with a rotating cyan ship, red thrust, drifting green
27
- rocks, yellow shots and magenta fragments. Pong's one-cell-high paddles and
28
- quadrant-block ball move in half-cell steps; doubling the presentation rate does
29
- not double gameplay speed. Asteroids wraps at the edges and shots break up rocks;
30
- its seeded autonomous flight is decorative, not player-controlled.
25
+ a connected snake eating food and growing, Pac-Man with a blue maze, pellets,
26
+ yellow mouth sprite and colored ghosts, or Asteroids with a filled cyan ship,
27
+ red thrust, white rocks, yellow shots and expanding collision fragments.
28
+ Ctrl+T/Ctrl+X cycles MISSION CONTROL → SESSION → the visible game pane.
29
+ While the game is active, Ctrl+G cycles Tetris → Snake → Pac-Man → Asteroids → Galaga → Frogger;
30
+ outside game focus Ctrl+G retains its swarm alias, and Ctrl+S opens swarm.
31
+ Enter/Esc returns to the unchanged mission draft. Modals and cancellation retain
32
+ priority; games never read input or render on a separate thread.
33
+ Pac-Man starts on an arrow, moves every 0.3 seconds and buffers the latest turn
34
+ until a corridor permits it. Walls stop movement. Eat every pellet to advance
35
+ the level; large yellow power pellets turn ghosts blue for seven seconds.
36
+ Ghosts chase through corridors, flee while frightened and return home when eaten.
37
+ Contact with a normal ghost ends the round, followed by a brief flash and a fresh
38
+ arrow-ready start. Small mazes use one ghost; larger mazes use three.
39
+ Maze walls are generated from a seeded spanning tree with extra loops, not a
40
+ fixed grid with shuffled pellets. Every pellet remains reachable, and each round
41
+ consumes fresh random geometry. Pac-Man and ghosts each occupy one terminal cell,
42
+ including in the smallest eight-cell console pane. The maze fills the available
43
+ interior with internal walls and connected corridors instead of sacrificing routes
44
+ for enlarged sprites. Directional mouth and ghost glyphs retain compact hints;
45
+ individual eyes and scalloped feet cannot be resolved at this size.
46
+ Galaga uses arrows to move and Space to shoot. Destroy formations and diving
47
+ enemies, dodge hostile shots, and clear the formation to advance a wave. A hit
48
+ shows an expanding explosion, then an input-ready restart. The cyan player ship is
49
+ a filled upward-pointing delta with a nose, spine and wings on a 2x4 Braille-dot
50
+ raster; movement and collision coordinates remain unchanged.
51
+ Frogger uses arrows to hop: avoid moving traffic, ride moving logs across water,
52
+ and enter each empty goal. Water, traffic, occupied goals and riding off the edge
53
+ cause a short death/recovery animation. Filling all goals advances the level.
54
+ Both games begin on input, fill small and large panes, and run automatic demos
55
+ when inactive. Their footer hints appear only while the game pane is focused.
56
+ Pac-Man, Galaga and Frogger demos advance one caller-owned simulation frame per
57
+ UI tick rather than building a full replay on a cold resize or session change.
58
+ The deterministic random-access `mini_cells` replay API remains available to
59
+ other callers; its first large-canvas call can take longer than cached frames.
60
+ Snake uses arrows; Tetris uses Left/Right, Down and Space to rotate; Asteroids
61
+ uses Left/Right to turn, Up/Down for thrust/reverse and Space to fire. Asteroids
62
+ wraps at the edges and shots split rocks. Inactive games run seeded automatic demos.
63
+ Pong is no longer selectable; the explicit `name: :pong` Banner APIs remain
64
+ compatible with existing callers.
31
65
  Its framed width in terminal cells equals the complete header's height in rows;
32
66
  the interior canvas is `(header height - 2)` cells on each side. This is a
33
67
  cell-square, not a pixel-square—terminal glyph cells are usually taller than wide.
@@ -43,14 +77,14 @@ color, holes and silhouette instead of recoloring neighbors when they touch.
43
77
  The board remains full-width; only its horizontal logical resolution changes.
44
78
  Completed rows intentionally flash white before collapsing; game-over and replay
45
79
  resets also use white holds/wipes. Ordinary locking never changes color or shape.
46
- Colored falling blocks support interiors up to 64 cells; the other artwork retains
80
+ Colored falling blocks, Pac-Man, Galaga and Frogger support interiors up to 64 cells; the other artwork retains
47
81
  its 16-cell limit. Above the applicable limit, art is centered and padded rather
48
82
  than clipped or stretched.
49
83
  One `PWN::Banner.mini_names` animation is randomly
50
84
  selected for the session and retained across redraws, model changes and resize;
51
85
  changing the active session selects again. Frames advance at the banner API's
52
- `mini_frame_seconds(name:)` cadence (0.05 seconds for Pong/Asteroids, 0.1 for
53
- blocks/snake) using monotonic time in the existing render loop—no extra animation
86
+ `mini_frame_seconds(name:)` cadence (0.05 seconds for Asteroids/Galaga, 0.1 for
87
+ blocks/Snake/Pac-Man/Frogger) using monotonic time in the existing render loop—no extra animation
54
88
  thread, input reader or provider call. This is decoration, not progress or
55
89
  telemetry. The frame keeps the existing border/title theme roles. Artwork comes
56
90
  from `PWN::Banner.mini_cells(branding: false)` with dedicated foreground/background curses pairs,
@@ -60,7 +94,7 @@ halves become a full block, not a half-block with its background lost). The `min
60
94
  retains its 60-frame, 0.1-second loops for other callers. Both miniature APIs
61
95
  default to their legacy branding; `branding: false` reclaims the label row and
62
96
  leaves undersized fallback panes blank. Colored replays contain
63
- 1800 frames (90 seconds for Pong/Asteroids, 180 for blocks/snake), use a local
97
+ 1800 frames (90 seconds for Asteroids, 180 for blocks/snake/Pac-Man), use a local
64
98
  per-session seed, and share a bounded six-replay cache. Settings
65
99
  wrap in the right-hand region without losing their label colors. A bounded,
66
100
  nonrecursive layout pass grows the header and square together. If the terminal
@@ -12,7 +12,7 @@ metadata:
12
12
 
13
13
  # PWN::Banner
14
14
 
15
- Static banners and pure, PWN-branded retro ASCII/color loops: falling_blocks, snake, pong, and asteroids. mini_frame returns fresh rows on a borderless canvas; use equal width and height (5..16) for complete square artwork. Dimensions clamp to 0..16 (colored blocks: 0..64). Set branding: false for an unbranded pane with the wordmark row reclaimed for play; tiny panes then stay blank. The caller samples mini_names once per session and advances the explicit frame index every mini_frame_seconds(name:) (0.05 for Pong/Asteroids, 0.1 otherwise). mini_cells replays 1800 seeded gameplay frames (MINI_FRAME_COUNT); mini_frame retains its legacy 60-frame ASCII loop. These are decorative game simulations, not interactive games or telemetry. mini_cells uses local Random.new with seed 73 by default; callers may keep a different integer seed per session. Six immutable replays are cached. Tetrominoes stack and clear; snake pursues food and grows; Pong rallies include imperfect paddle tracking, misses and score pips between serves. Blocks use horizontal half-cells with independent foreground/background colors, filling the pane below PWN (up to 64 cells). Other games retain quadrant pixels; snake stays centered with food/head color priority. White line-clear flashes and game-over/reset wipes are intentional. Pong uses half-cell motion and one-cell-high paddles without speeding up play. Asteroids uses five-pixel cyan arrowheads on a half-cell grid, sparse small white rocks, yellow shots and red thrust; wrapped collisions split real rocks. Static Banner modules autoload only when needed. For more information, see: http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
15
+ Static banners and pure, PWN-branded retro ASCII/color loops: falling_blocks, snake, pacman, asteroids, galaga and frogger. mini_frame returns fresh rows on a borderless canvas; use equal width and height (5..16) for complete square artwork. Dimensions clamp to 0..16 (colored blocks/Pac-Man/Galaga/Frogger: 0..64). Set branding: false for an unbranded pane with the wordmark row reclaimed for play; tiny panes then stay blank. The caller samples mini_names once per session and advances the explicit frame index every mini_frame_seconds(name:) (0.05 for Asteroids/Galaga, 0.1 otherwise). mini_cells replays 1800 seeded gameplay frames (MINI_FRAME_COUNT); mini_frame retains its legacy 60-frame ASCII loop. Inactive panes use seeded demos; MiniGame supplies caller-owned playable state. mini_cells uses local Random.new with seed 73 by default; callers may keep a different integer seed per session. Six immutable replays are cached. Tetrominoes stack and clear; snake pursues food and grows; Pac-Man eats pellets in connected mazes with chasing ghosts, energizers, recovery and new levels. Blocks use horizontal half-cells with independent foreground/background colors, filling the pane below PWN (up to 64 cells). Other games retain quadrant pixels; decorative snake stays centered with food/head color priority. White line-clear flashes and game-over/reset wipes are intentional. Pac-Man and ghosts occupy one cell each so even eight-cell panes retain internal walls and corridors. Directional mouths and compact ghost glyphs trade eye detail for maze geometry; spanning-tree mazes vary each round. Galaga has formations, dives, hostile fire, explosions and advancing waves; arrows move and Space fires. Frogger arrows hop through moving traffic and onto carrying logs to fill goals, recover from deaths and advance levels. Both start on input, run automatic demos when inactive and retain the UI owner. Legacy name: :pong remains accepted by direct APIs but is not selectable. Asteroids uses filled cyan ships on a 2x4 Braille raster, sparse small white rocks, yellow shots and red thrust; wrapped collisions split real rocks. Static Banner modules autoload only when needed. For more information, see: http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
16
16
 
17
17
  ## When to use
18
18
 
@@ -59,8 +59,78 @@ module PWN
59
59
  # (:escalation_persona) — the local model still produces the final
60
60
  # answer so Learning/Metrics stay attributed to :ollama.
61
61
  module Loop
62
+ # Provider calls run on a child. Thread#raise does not wake a native read,
63
+ # so the owner waits in Ruby and leaves within one poll on cancel.
64
+ class ProviderWindow
65
+ def request_model_cancel
66
+ @mutex.synchronize do
67
+ @cancel_requested = true
68
+ if @phase == :model && !@model_signal_sent
69
+ @model_signal_sent = true
70
+ @owner.raise(Steering::ModelCancelled)
71
+ end
72
+ end
73
+ end
74
+
75
+ def model(messages:, &block)
76
+ result = nil
77
+ worker = nil
78
+ Thread.handle_interrupt(Steering::ModelCancelled => :never) do
79
+ checkpoint(messages: messages, phase: :model)
80
+ raise Steering::ModelCancelled if cancel_requested?
81
+
82
+ worker = provider_thread(&block)
83
+ Thread.handle_interrupt(Steering::ModelCancelled => :immediate) do
84
+ loop do
85
+ raise Steering::ModelCancelled if cancel_requested?
86
+
87
+ break if worker.join(0.05)
88
+ end
89
+ end
90
+ result = worker.value
91
+ ensure
92
+ abandon_provider(worker)
93
+ @mutex.synchronize { @phase = :boundary }
94
+ begin
95
+ Thread.handle_interrupt(Steering::ModelCancelled => :immediate) { nil }
96
+ rescue Steering::ModelCancelled
97
+ nil
98
+ end
99
+ end
100
+ result
101
+ rescue Steering::ModelCancelled
102
+ nil
103
+ end
104
+
105
+ private
106
+
107
+ def cancel_requested?
108
+ @mutex.synchronize { @cancel_requested }
109
+ end
110
+
111
+ def provider_thread(&block)
112
+ parent = Thread.current
113
+ locals = parent.keys.select { |key| key.to_s.start_with?('pwn_') }.to_h { |key| [key, parent[key]] }
114
+ variables = parent.thread_variables.select { |key| key.to_s.start_with?('pwn_') }.to_h { |key| [key, parent.thread_variable_get(key)] }
115
+ Thread.new do
116
+ Thread.current.report_on_exception = false
117
+ locals.each { |key, value| Thread.current[key] = value }
118
+ variables.each { |key, value| Thread.current.thread_variable_set(key, value) }
119
+ Thread.handle_interrupt(Steering::ModelCancelled => :never, &block)
120
+ end
121
+ end
122
+
123
+ def abandon_provider(worker)
124
+ return unless worker&.alive?
125
+
126
+ worker.raise(Steering::ModelCancelled)
127
+ worker.join(0.2)
128
+ worker.kill if worker.alive?
129
+ end
130
+ end
131
+
62
132
  # Request-owned input broker; only the provider window is interruptible.
63
- class Steering
133
+ class Steering < ProviderWindow
64
134
  INPUT_LOCK = Mutex.new
65
135
  INPUT_OWNERS = {} # rubocop:disable Style/MutableConstant -- guarded by INPUT_LOCK
66
136
 
@@ -80,12 +150,14 @@ module PWN
80
150
  attr_reader :reader
81
151
 
82
152
  def initialize(input:, output:)
153
+ super()
83
154
  @input = input
84
155
  @output = output
85
156
  @owner = Thread.current
86
157
  @mutex = Mutex.new
87
158
  @pending = []
88
159
  @phase = :boundary
160
+ @cancel_requested = false
89
161
  end
90
162
 
91
163
  def submit(text)
@@ -127,23 +199,6 @@ module PWN
127
199
  raise Restart.new(messages, instructions)
128
200
  end
129
201
 
130
- def model(messages:, &block)
131
- # Drain an in-flight signal inside this rescue, never in a tool.
132
- Thread.handle_interrupt(ModelCancelled => :never) do
133
- checkpoint(messages: messages, phase: :model)
134
- Thread.handle_interrupt(ModelCancelled => :immediate, &block)
135
- ensure
136
- @mutex.synchronize { @phase = :boundary }
137
- begin
138
- Thread.handle_interrupt(ModelCancelled => :immediate) { nil }
139
- rescue ModelCancelled
140
- nil
141
- end
142
- end
143
- rescue ModelCancelled
144
- nil
145
- end
146
-
147
202
  def with_reader
148
203
  require 'io/console'
149
204
  input_id = @input.fileno