Commit Graph

61 Commits

Author SHA1 Message Date
pragent-bot 60989cd72f docs: add CONTRIBUTING.md (testing requirements + pragent notes)
Specifies what counts as "tested" for this repo, the local test
commands the bot treats as gates, and how pragent reads the repo
config (`.pr-review.json` + static architecture.md from Nexus
raw-hosted).
2026-08-20 18:49:06 +00:00
pragent-bot 6b2a73678f chore(pr): add .gitea/pull_request_template.md (pragent workflow)
Auto-loaded by Gitea on every new PR. Lists the surfaces and risks
pragent scores you on; tells the author to add the AI-REVIEW label
to trigger a bot review.
2026-08-20 18:48:58 +00:00
Marcos Paulo 8ddc22409e Merge pull request #5 from feat/void-protection 2026-08-20 15:09:50 -03:00
Marcos Paulo bc7a88b9e2 chore: sync .pr-review.json with main 2026-08-20 15:08:41 -03:00
marcos 8a34565f3c chore(pr-review): re-trigger bot review against updated .pr-review.json 2026-08-20 17:56:03 +00:00
marcos f24af7ab08 chore(pr-review): add .pr-review.json with canalhandia-specific steer + repo context
Wires the new ADDITIONAL_CONTEXT_URL feature on the central webhook service
(see gitea_admin/pragent e8ebc54) — fetches a static maintainer-curated brief
from the Nexus raw-hosted repo at review time. The doc distils the house
rules so the agent doesn't have to re-derive them from the diff each PR:

* chat messages are immutable after send (counts baked into buttons freeze)
* translatable components + singular-form rule for pt-BR sentences
* Geyser/Bedrock can't click — every click has a typed fallback
* vanilla stats only — <world>/players/stats/<uuid>.json, NOT <world>/stats
* reactions keep counting late (reacao-validade-minutos)
* Floodgate is optional runtime, never compile-time

Plus the canonical focus/instructions/languages config: java, balanced style,
claude-sonnet-5 cost target, JDK 25 build note.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-20 17:54:32 +00:00
Marcos Paulo b6e508c8c9 fix(pr-reviews): complete all PR comments resolutions and test coverage 2026-08-20 14:51:06 -03:00
Marcos Paulo cc5c983e41 fix(pr-reviews): address spawner reset, numeric name resolution, and activation validation 2026-08-20 14:48:21 -03:00
pragent test 07e5bb6018 chore: retrigger pragent review (post format-overhaul) 2026-08-20 17:17:44 +00:00
pragent test f2a19a729a chore: retrigger pragent review 2026-08-20 16:38:42 +00:00
pragent test dfb84047ec chore: retrigger pragent review 2026-08-20 16:38:18 +00:00
pragent test 8821e6d2c0 chore: retrigger pragent review (no-op) 2026-08-20 16:36:28 +00:00
Marcos Paulo 1ff830537e feat(chunkloader): add fuel charging, duration timers, and /chunkloader tempo 2026-08-20 13:19:57 -03:00
pragent-bot da4a1777a7 chore: add .pr-review.json for pragent 2026-08-20 16:14:05 +00:00
Marcos Paulo ce3b1ebbd9 feat(chunkloader): support custom names, anvil placement, and pause/resume 2026-08-20 13:13:06 -03:00
Marcos Paulo 5a6bb9a13d feat(chunkloader): add /chunkloader limite command and tab completion 2026-08-20 12:55:38 -03:00
Marcos Paulo 4b9507b36e feat(chunkloader): add mob spawning simulation, spawner activation and despawn protection 2026-08-20 12:53:19 -03:00
Marcos Paulo 4261e8b660 fix(chunkloader): use setChunkForceLoaded for full block/entity/redstone ticking 2026-08-20 11:16:24 -03:00
Marcos Paulo 2c7f5f98ad fix(review): check event.getKeepInventory(), equipment slot hand, and world height bounds 2026-08-20 10:21:11 -03:00
Marcos Paulo c3c4906e11 fix(review): address review comments and add .pr-review.json for pragent 2026-08-20 10:14:18 -03:00
Marcos Paulo d93711e5c1 fix(chunkloader): deliver anchor directly to inventory on break and protect against accidental interaction 2026-08-20 10:01:59 -03:00
Marcos Paulo 24ab63b9be feat(void): add void death item protection with safe chest and inventory fallback 2026-08-20 09:52:48 -03:00
Marcos Paulo e059ca6563 feat(chunkloader): Módulo nativo de Chunk Loader com limites LuckPerms e BlueMap (#4) 2026-08-19 15:31:22 -03:00
Marcos Paulo e70df329b3 fix(chunkloader): release tickets on disable/toggle, fix BlueMap world matching, and prevent item loss on remove 2026-08-19 15:09:44 -03:00
Marcos Paulo e71babca1c fix(memory): align substring index with trimmed input in extractHeuristicFact 2026-08-18 22:00:42 -03:00
Marcos Paulo 01ada6d987 feat(chunkloader): add native chunk loader module with LuckPerms limits and BlueMap support 2026-08-18 21:45:51 -03:00
masi 01e6c28fef Merge pull request 'feat(ia): add Judite & Narrador personas, per-player AI selection, persistent memory, and event reactivity' (#3) from feat/ia-personalities-and-memory into main
Reviewed-on: #3
2026-08-18 22:52:02 +00:00
Marcos Paulo 998757d610 fix(ia): make memory persistence async, wire heuristic fact extraction and /ia lembrar, and add tag and event tests 2026-08-18 19:42:46 -03:00
Marcos Paulo 2220f11e64 fix(ia): prevent private notes leak in places tool, guard recordTurn against disk failure, and add tests 2026-08-18 19:15:02 -03:00
Marcos Paulo ea55813019 feat(ia): add Judite & Narrador personas, per-player AI selection, persistent memory, and event reactivity 2026-08-18 19:00:11 -03:00
masi dafd96a4b6 i18n: per-player EN/PT via Adventure GlobalTranslator (#1)
Foundation + commands module of the i18n spec.

- I18n registry/loader + Lang.tr facade + reloadI18n
- PT source-of-truth bundle + EN translation
- CanalhandiaCommand player-facing strings migrated; admin-tuning/help/enum-labels deferred
- I18nTest: parity + per-locale render + pt_BR fallback; 316/316 green

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-12 15:53:27 +00:00
marcos c1a6b9730f docs: document the IA module
README: add ia to the modules table, the four ia permissions, and a full
IA section covering commands, profile switching, operator corrections,
memory/context, and the key + rate-limit model. Notes the safety boundary
(no tools, reply never executed, leading slashes stripped) and which config
values are baked vs live.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 03:56:58 +00:00
marcos a3b2e7dd27 fix(ia): wire canalhandia.ia.privado gate, tighten subcommand hijack
Review of Task 11 (commit 5f616d3) flagged a blocking spec regression:
canalhandia.ia.privado was declared (default: true) but never checked --
ia(sender, args, isPrivate) only checked canalhandia.ia, so /iap silently
required canalhandia.ia and the privado perm did nothing.

Per operator decision, /ia and /iap are now separate gates, both default
op, so LuckPerms can grant them independently (operator + permitted only,
not everyone). /ia needs canalhandia.ia; /iap needs
canalhandia.ia.privado.

Also fixes three should-fix findings:
- Subcommand hijack is no longer greedy: perfil and feedback only hijack
  when the second token is one they act on (a known profile key, or "ruim"),
  so "/ia perfil do servidor" and "/ia feedback do mapa?" fall through and
  are asked. corrigir stays greedy (a correction always reads the rest).
- AiProfile.isValid tells a real key from the PRECISO fallback, so /ia perfil
  blah no longer silently switches to PRECISO.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 03:54:01 +00:00
marcos 5f616d3d99 feat: /iap, reactions on answers, feedback and /ia corrigir 2026-08-06 03:43:19 +00:00
marcos a3f40d1408 fix: Ai review fixes — quit-race, token overflow, comment + nits
- Guard conversations.remember() on asker==null: a PlayerQuitEvent
  forgets the history (carry-forward #6), and re-adding here after the
  quit would resurrect it. lastAnswer stays regardless so /ia corrigir
  can still correct the last answer.
- Saturate the retry token ceiling: max_tokens near Integer.MAX_VALUE
  would overflow to a negative budget sent to the API.
- Tighten the constructor comment: aiUrl/aiWikiChars/aiMemoryExchanges/
  aiMemoryMinutes are baked; everything else (aiProfile, aiModel,
  aiMaxTokens, aiTemperature, aiInstructions, aiServerContext) is read
  live, not just aiProfile.
- Drop unused import java.util.List; add trailing newline.
2026-08-06 03:32:31 +00:00
marcos 5b13ec7713 feat: ground answers in the wiki, recipes, corrections and memory
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 03:19:54 +00:00
marcos 7940a8fc66 fix: guard Corrections against async read from compose
all() returns List.copyOf(entries), which iterates; add()/load()
structurally modify. Once Task 10 wires all() into runTaskAsynchronously
and Task 11 wires add() from the main-thread /ia corrigir command,
the race throws ConcurrentModificationException. Guarded by entries'
own monitor like Conversations: file parse and YAML save stay outside
the lock, only fast in-memory work is under it.
2026-08-06 03:12:27 +00:00
marcos 5cfa580997 feat: operator corrections injected into similar questions 2026-08-06 03:09:11 +00:00
marcos aee991bb7b feat: IA profiles, server context and a workable token ceiling
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-06 03:03:53 +00:00
marcos e0d7c289a8 feat: short per-player conversation memory
Guarded by a monitor: Ai calls this from runTaskAsynchronously and
history() prunes while it reads, so an unsynchronised map would throw
ConcurrentModification into a player's answer or corrupt itself on resize.

Expiry compares nanoTime differences rather than wall-clock instants, so
a zero window expires deterministically and an NTP step backwards cannot
leave entries stamped in the future and unexpirable. Player count is
capped LRU, since expiry only prunes players who ask again.
2026-08-05 16:23:57 +00:00
marcos c5d8f44bf2 fix: restrict API keys to printable ASCII, and harden response parsing
Fuzzing all 65,536 char values, not the 256 of the previous pass, shows
the JDK rejects 65,312 of them from a header value and every single one
echoes the value back. The previous deny-list covered 33. A key file
saved by Notepad or PowerShell Out-File carries a U+FEFF byte order
mark, which passed both filters and reached the quoting validator.

Ai.cleanKey and HttpFetcher.checkBearer now allow printable ASCII only,
an allow-list of the 94 characters a bearer token is made of, which
cannot drift out of what the JDK accepts. Demonstrated invariant:
cleanKey output ⊆ checkBearer accepts ⊆ JDK accepts, 0 violations.

Ai.call gains the same check and its failure log is now redacted; that
was the one path where the proof-of-concept leak surfaced.

MiniMax.message and answer now check JSON types before assuming them.
Gson throws unchecked on JSON that parses but has the wrong shape, and
both run outside post()'s try, so {"choices":["str"]} escaped to an
async Bukkit worker as a stack trace instead of the promised null.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 16:18:32 +00:00
marcos 88fe9c9695 fix: stop a malformed API key reaching a log line
HttpRequest.Builder.header() quotes the offending header value back in
its IllegalArgumentException. Fuzzed on temurin-25: all 32 control
characters it rejects echo the value, so a key carrying any of them ends
up in whatever log catches the throw. A key file with a comment on line
two survives trim() and is enough to trigger it.

Ai.cleanKey now takes the first non-blank line and drops control
characters, so a malformed key never forms. HttpFetcher.checkBearer
rejects one anyway before the request is built, with a message naming
only the position, so no future caller has to remember to redact. Its
reject set is a strict superset of the JDK's.

Also renames MiniMax.Msg to MiniMax.Turn: the package already has a
top-level Msg, the chat-formatting helper, which the record shadowed
inside MiniMax.java.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 16:05:22 +00:00
marcos 6e0167cff1 feat: MiniMax client with forced tool call for wiki term selection
Term selection is a forced tool call rather than free text: measured 5/5
against 2/7 for a free-text extraction call, because a tool argument is
structured output and survives the hidden reasoning eating the budget.
With tool_choice auto the model skipped the search on exactly the
questions it was most likely to get wrong.

Failures are reported through an optional warn consumer, as Wiki does,
and the endpoint is injectable for MiniMax's regional hosts.

Warnings are redacted of the key: a key with an embedded newline makes
the JDK throw invalid header value: "Bearer sk-...", quoting the whole
value back, and that lands in the generic call-failure path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 16:00:46 +00:00
marcos 62497556a5 fix: never resolve a recipe question to the wrong item
The substring fallback in materialFor returned plausible recipes for
items the player did not ask about. "Redstone Repeater" tied on length
and resolved to REDSTONE, handing someone asking about repeaters the
recipe for redstone dust; "Book and Quill" gave BOOK, "Minecart with
Chest" gave MINECART, "Rabbit's Foot" gave RABBIT, "Chestplate" gave
CHEST. That is the exact failure this feature exists to remove, and it
is worse than no answer because the model states it confidently.

Dropped it. Exact match plus a hand-checked alias table, else null. No
suffix rule either: "axe" is a suffix of "pickaxe", so tool and armour
families would fail the same way. Beds and wool now return null, which
is correct.

brewing() matched keys as bare substrings and hijacked real questions:
"salto" inside basalto, "cura" inside curar, "forca" inside reforcar.
Since describe() answers brewing first, each took over the whole answer.
Now gated on the question mentioning a potion, and matched on word
boundaries.

Table corrections: "Fogo do Dragao" is not an item, it is Bafo do Dragao
giving a lingering potion; Frasco de Agua, not Garrafa de Agua, which is
the empty bottle; Pe de Coelho; Fatia de Melancia Reluzente. Added the
in-game item names players actually type (Agilidade, Dano) and stripped
hyphens so Mestre-Tartaruga reaches the table.

describeShapeless no longer emits a dangling "Sem formato: ".

describeChoice could not be covered after all: constructing a
MaterialChoice initialises org.bukkit.Registry, which needs a server,
and the class is sealed so it cannot be faked. Verified in Task 13.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 15:54:40 +00:00
marcos dba64df917 feat: ground recipes on the server, translated via wiki langlinks
Crafting recipes come from Bukkit.recipeIterator(), which is authoritative
for this exact version. Player questions are Portuguese and Material names
are English, so the subject of the question is searched on the pt wiki and
prop=langlinks&lllang=en gives the English title, which uppercases onto the
enum constant.

Matching material names against the question directly does not work.
Measured over twenty realistic pt-BR questions it resolved 1 of 20, and that
one ("tridente" containing "trident") by coincidence rather than
translation. Through langlinks the same twenty resolve 17 of 20.

Brewing is not exposed by Bukkit at all: there is no brewing Recipe type,
PotionBrewer has no getter or iterator, and vanilla brewing is hardcoded in
PotionBrewing rather than registered as a recipe. Potions therefore come
from a hand-written pt-BR table. This is what actually answers the
fire-resistance question that motivated the feature; recipeIterator() alone
never could have.

Ingredients are read through getChoiceMap/getChoiceList. The deprecated
getIngredientMap/getIngredientList collapse a choice to one arbitrary stack,
printing "oak planks" where the recipe accepts any plank.

The design doc claimed recipeIterator() closed the potion case. It did not;
corrected to record what is true.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 15:40:36 +00:00
marcos 0f3bdf209d fix: report wiki failures instead of grounding silently on nothing
Every failure mode returned null, which the caller cannot tell apart
from a term the wiki has no article for. A 403 would revert /ia to the
confidently wrong answers grounding exists to stop, against a clean log.

Also clamps maxChars so a config of 0 cannot switch grounding off for
good, restores the interrupt flag on disable, and distinguishes a
malformed response from an outage in the log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 15:25:45 +00:00
marcos 13fb87fb8b feat: fetch full articles from the Portuguese Minecraft Wiki 2026-08-05 15:17:01 +00:00
marcos 9ffcf816ea feat: add Fetcher seam with an identifying user agent 2026-08-05 15:11:11 +00:00
marcos 3d2150d2a6 docs: scope the foreign-script and surefire claims to what the code does
hasForeignScript detects by alphabet, so Latin-script leakage such as
the observed French "contiennent" is not caught. Say so in the javadoc
and admit the gap in the design doc rather than implying coverage.

failIfNoTests catches a misplaced or misnamed test class, not a
disabled one: an @Disabled class still reports as skipped and the
build stays green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 15:09:37 +00:00
marcos 4f427659bb refactor: extract AiText and detect foreign-script leakage
Moves sanitise out of the Bukkit-bound Ai class so it can be unit
tested, and adds hasForeignScript to catch the CJK words the model
intermittently drops into Portuguese answers.

Colour-code stripping now removes the code character too: replacing
only the section sign left "§c" reading as a stray "c" in chat.

Surefire now fails on an empty suite, so a misplaced or disabled test
class cannot pass as a green build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jSvSTrG4qsniLSr6TpC6
2026-08-05 15:03:00 +00:00