Skip to content

feat(mmx-octopus): add our own agent so her character sheet lives in the system prompt (v1.8.0) - #107

Open
antianqi wants to merge 6 commits into
MiniMax-AI:mainfrom
antianqi:fix/selfagent-v180
Open

antianqi wants to merge 6 commits into
MiniMax-AI:mainfrom
antianqi:fix/selfagent-v180

Conversation

@antianqi

@antianqi antianqi commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does (v1.8.0)

Adds one agent of our own to the Host agent configuration, so that the MMX Octopus character sheet can live in an agent system prompt instead of only inside the message.

This follows the official answer to the owner question: a script may create a new agent in the mcode agent configuration; adding one should be fine as long as existing agent configuration is not modified. This PR does exactly that, and nothing else.

Product default changed: a dedicated agent is now the default (2026-11-04, commit 0349167)

This corrects the statements below. Earlier follow-ups said the default adds nothing and
writes not a single byte outside the plugin data directory. That is no longer the default.
The product default is now create a dedicated agent, because that is what gives the best
experience: her character sheet then lives in that agent's system prompt, so her personality
is crisper and her replies more lifelike, instead of travelling inside every message where
the Host context compaction may drop it.

What did not change - the opt-out is still one click. The two-way choice stays, it is still
asked on first run, and picking use an existing agent still means the plugin writes not a
single byte
outside its own data directory and never touches the agent it could have added.
The zero-write behaviour is now the opt-out rather than the default.

The disclosure follows. Both READMEs now say that by default the plugin adds one agent
file, and that the use an existing agent tier is the zero-write opt-out. The three hard lines
are unchanged: still only add (never modify an existing agent), still never guessed
(only the one provable path), still never throws (every failure degrades and says why).

What was actually changed in the code. Not one constant - "what the default is" now lives
in exactly one place: DEFAULT_AGENT_MODE and normalizeAgentMode in node/agentpick.mjs.
The state machine, the server route and the panel all go through it; the page reads the value
the server injects, so no second default can drift. Before this, that rule was written in nine
places, and the server.mjs fallback even pointed the other way.

Self-test: 875 checks, 0 failures (new group round 47, 10 checks). The checks judge the
assembled behaviour, not source text: they run the real PetState, they POST to the real
/api/agent-mode route, and they execute the panel's own normalization function. Six
falsification experiments all turn the judgement red - including the one that matters most:
hard-coding the default in the page (instead of reading the injected value) is caught.

One weakness of mine was caught while doing this: the first version of the page check compared
the page result with the server result, and since both defaults were own, a page that
hard-coded the default still passed - the check was fake. It now requires the page to follow
the injected value, so the two can no longer be told apart by accident.

Review follow-up (2026-11-04, commit e7415ab)

  • Must fix 1 - the directory is no longer guessed. The eight-level walk-up that could land in a user project is gone. The root now comes from exactly one provable path (hard line 2 below). The self-test has a case for the exact scenario in the review - a session workspace inside a user project that contains a folder named agents - and asserts that not a single byte is written there; re-inserting the old walk-up into the module turns exactly that check red and names the path it would have written.
  • Must fix 2 - versions aligned. Both READMEs now say 1.8.0, matching plugin.json and package.json.
  • Optional 1 - the extra session. The session the plugin creates to learn that path is now reused as her session instead of creating another one, and both READMEs say so. Per the Host docs, sessions a Mini App starts itself belong to the Mini App and do not appear in the chat list.
  • Optional 2 - npm run check. The CLI moved to miniapp/scripts/agentprov-cli.mjs: the Host scans miniapp/node/** as runtime code, where stdout writes warn. node scripts/validate.mjs now reports plugins/antianqi/mmx-octopus: OK with no warnings, and 87/87 tests pass.
  • Optional 3 - the choice to opt out. Implemented in commit ba3bb32 - see the second review follow-up below. The panel now has a two-way choice instead of a switch. ⚠️ The default changed again afterwards (see the section above): the dedicated agent is now the default, and use an existing agent is the zero-write opt-out.

Review follow-up (2026-11-04, commit ba3bb32) - Optional 3: the choice lives in the panel

The review asked how a user could opt out; the only answer was create an empty agent named mmx by hand - not easier. The panel now has a two-way choice:

  • Use an existing agent (⚠️ the opt-out, no longer the default - see the section above): the plugin adds nothing and writes not a single byte outside its own data directory - the v1.7.0 behaviour.
  • Create a dedicated agent (⚠️ now the default - see the section above): <Host agent dir>/mmx-octopus-agent/agent.md is created, exactly as described above. If that cannot be done (name taken / directory not provable / write or verify failed), the panel says why instead of silently falling back.

Two details worth flagging:

  • Switching the choice restarts her session (the Host cannot change the agent of a live session). The new session opens with a short recap, so the thread continues.
  • The dedicated tier uses that agent only if the file really is ours: if the name is already taken by somebody else's agent, that agent is never used as if it were ours. That was a real inconsistency found while writing the disclosure - the panel said could not create it, continuing with an existing agent while the picker could still have selected the foreign agent.

Both READMEs state that by default one agent file is added, that use an existing agent is the zero-write opt-out, and that the old workaround (create an agent named mmx by hand) is gone - the panel is the only entry point. ⚠️ The "by default nothing is added" wording in this paragraph was true for commit ba3bb32 and was corrected in commit 0349167 (see the section above).

First run asks the same choice (2026-11-04, commit de64134)

The two-way choice used to live only in the settings panel, so a first-time user never met it. It is now asked on first run, on the opening card:

  • the card asks for the tier first (use an existing agent / create a dedicated agent) - pre-selected to create a dedicated agent, the product default (see the section above),
  • then for her name and how she addresses you,
  • and all three go out in one submission.

Two details worth noting:

  • The same control now exists in two places, so both share one rendering (a data-agentmode-group marker plus a single paintAgentMode selector) - the two copies cannot drift.
  • While the opening card is open, a snapshot refresh no longer overwrites the tier the user is still choosing.

Self-test at that point: 865 checks, 0 failures (new group round 46, 7 checks); the total has since grown to 875 - see below. One of the falsification experiments exposed a weak check of mine: it only counted how often the shared marker appears in the file, so taking it off one of the two places still passed - it now checks each container separately.

Why

  • The Host gives a Mini App three channels (hosted MCP / message.append into the input box / the Agent session API) - there is no "inject a system prompt" channel.
  • So the character sheet could only travel inside the message (weak channel); the Host context compaction may drop it.
  • An own agent gives it a second home (the agent system prompt), which survives compaction.

The three hard lines

  1. Only ADD <Host agent dir>/mmx-octopus-agent/agent.md. No existing agent is modified. If that name is already taken by an agent that is not ours, the plugin writes nothing and does not use it - she keeps working on the default agent.
  2. The Host agent directory is located only from a path that proves it: the workspace of a session the plugin itself created. The Host sets that workspace to the Mini App own agent directory (<agents root>/<agent>/miniapps/<plugin-id>), and the plugin takes its parent. It is never guessed - no walk-up looking for a folder named agents, and never derived from dataDir. If it cannot be identified, the plugin writes nothing.
  3. Every failure (unknown directory / name taken / write error / verify error) returns an error result: it never throws and never blocks her from talking. The degraded path is the old behaviour (the character sheet travels with the message).

What is never written, at any time

  • ⚠️ The default tier does add one file. With create a dedicated agent (now the default) the plugin creates <Host agent dir>/mmx-octopus-agent/agent.md on first run - that is the one and only file it writes outside its data directory. Everything below is about what is never written, rewritten or touched.
  • The file is only written when it does not exist yet. An existing file is never rewritten - not on a rename, not on a package upgrade, not on a restart.
  • Renaming her writes only state.json. The /api/naming route touches the plugin data directory and nothing else; the agent file is not opened, not rewritten, and its display name does not follow the setting.
  • A rename never changes the agent she talks to. It only marks the session for a rebuild (the Host cannot change the agent of a live session).
  • The only module in the package that may write a text file is agentprov.mjs, and its only target is the one agent file above.
  • Nothing is ever written into a project. The Host agent directory is only reached through the one provable path above, so a project that happens to contain a folder named agents can never be written into.

Behaviour

  • Which agent she uses is the user's choice, in the panel (new): use an existing agent or create a dedicated agent. ⚠️ The dedicated agent is the default now (see the section above); use an existing agent is the zero-write opt-out - in that tier the plugin adds nothing and writes not a single byte outside its own data directory. The choice is asked on first run as well (see the section below).
  • Agent priority follows that choice - use an existing agent: explicitly chosen > the user own mmx agent > the default agent (ours does not take part at all); create a dedicated agent (the default): explicitly chosen > ours > the user own mmx agent > the default agent. If you already have an agent named mmx, nothing of yours is touched either way.
  • Her session moves onto the new agent only while she has not spoken yet (the Host cannot change the agent of a running session, so moving means a new session - doing it before the first message loses no history). With history, the current session is kept and the agent file is still added for the next one.
  • Writes are atomic (temporary file, then rename), one write at a time, and only when the file does not exist yet.

Disclosure (both READMEs)

The English and Chinese READMEs now state which file is written, what is in it, that nothing else is touched, how the directory is found, how it is written, how to remove it and how to pick up a new version (delete the directory - the file itself is never rewritten in place).

Verified on a real machine (2026-11-04)

The two tiers were exercised on a live install - clicking in the panel, not only reading the code:

  • Dedicated tier: picking create a dedicated agent and then sending one message produced <Host agent dir>/mmx-octopus-agent/agent.md (9548 bytes, with the <!-- mmx-octopus:self-agent --> marker in it), and her session then ran on agentName: mmx-octopus-agent - the Host listed it with display name 小章鱼, and the whole character sheet (persona, appearance, and the 28 speech rules) was in that agent`s system prompt.
  • Existing tier: switching to use an existing agent wrote nothing - the file`s timestamp and size were unchanged, and switching back did not rewrite it either (only add, never rewrite).
  • Nothing outside the agent dir: a sweep of every file changed under the Host data directory during that test found exactly one new host-side file - the agent file above. Everything else was the plugin`s own data directory.

One timing detail, stated plainly because it is a real behaviour and not a defect: switching the tier records the choice; the file is created on the next turn that goes through the session path (a message, idle talk, a diary entry). Creating it at the moment of the click is not possible in general, because the Host agent directory can only be proven from a session the plugin created itself - a fresh install has no such session yet, so it would have to fail with could not identify the Host agent directory and then create the file on the next turn anyway. Deferring it is therefore the behaviour that actually succeeds.

Verification

  • Node self-test: 875 checks, 0 failures (the count grew with rounds 45-47; see below). New group round 44 (10 checks) with 6 negative injections - all six turn the judgement red:
    • dropping the "never touch an existing file" guard,
    • pointing the target at the user own mmx agent,
    • deriving the Host agent directory from dataDir,
    • removing the shape check on the Host path, so that any path would be accepted,
    • breaking the agent priority,
    • writing a Host agent file from the rename route.
  • Round 44 also runs a behaviour case where the session workspace is a user project that contains a folder named agents (<tmp>/code/agents/foo): not a single byte may be written there.
  • The earlier "never writes a Host file" checks were rewritten as behaviour assertions: the only module allowed to touch an agent file is agentprov.mjs, and its target must be the one agent we own.
  • New group round 45 (7 checks) covers the panel choice: the two tiers of priority, the default tier coming from the single source of truth (and being persisted and part of the snapshot), that only the dedicated tier ever creates our agent, that the panel control and its route exist, and that a foreign agent owning the name mmx-octopus-agent is never used as ours. Two negative injections turn it red (changing the default tier / breaking the tier guard).
  • The judgements were falsified against the real code, not only against their own injections: removing the tier guard from server.mjs turns exactly one check red and names it; breaking the pure function behind the agent choice turns exactly one check red; breaking the server side of that same choice does too.
  • New group round 46 (7 checks) covers the first-run card: the tier choice is on that card, it sits before the name and the address, it is pre-selected to create a dedicated agent, both places share one rendering, and the card submission carries the tier out with the names. Two negative injections turn it red (deleting the choice from the card / dropping the tier from the card submission).
  • Every one of those seven checks was falsified against the real code: removing the shared marker from the card turns exactly one check red, dropping the tier from the card submission turns two red, swapping the two name fields turns the order check red, and flipping the card's default turns the default check red.
  • New group round 47 (10 checks) covers the default itself: that the default is create a dedicated agent, that the state machine really starts in that tier and only degrades when use an existing agent was chosen explicitly, that the route really normalises that way, that the panel reads the injected value instead of writing its own default, and that both control places pre-select the dedicated tier. Six falsification experiments all turn the judgement red: the source of truth flipped back, the state machine losing the explicit-downgrade rule, the route hard-coding its own default, the panel hard-coding its default, and the two control places pre-selecting the wrong tier. The round-47 checks judge the assembled behaviour - the real PetState is run, the real route is POSTed to, and the panel's own function is executed.

Known limits

  • If the plugin cannot identify the Host agent directory from the one provable path, nothing is written and she stays on the default agent with the per-message character sheet (the previous behaviour).
  • An installation that already ran keeps the tier it stored. The new default applies to a fresh install; if an earlier run already stored use an existing agent, that tier stays until you pick create a dedicated agent in the panel - one click, and the panel is the only place it lives.
  • ⚠️ By default one agent file IS added now (this changed in commit 0349167, see the section above). To keep the v1.7.0 zero-write behaviour, pick use an existing agent in the panel - then the plugin writes nothing outside its own data directory and never touches the agent it could have added.
  • The added file is not rewritten in place, so after a package upgrade it keeps the older character sheet until the directory is deleted.
  • The new agent appears in the Host agent list under the name mmx-octopus-agent (display name 小章鱼).

…the system prompt (v1.8.0)

Official answer (asked by the owner): a script may create a NEW agent in the Host agent
configuration - "adding one is fine as long as existing agent configuration is not modified".
This PR does exactly that, and nothing else.

What it does
- New module miniapp/node/agentprov.mjs: locates the Host agent directory (ONLY from paths the
  Host itself returns - session workspace / agents() list; never derived from dataDir), renders
  agent.md (her character sheet + the three identity sections + the frontmatter the Host parses),
  and writes it as ONE new file.
- New module miniapp/node/identity.mjs: the single reader for the identity sheet, shared by the
  per-message copy and the agent file (one judgement, one place).
- agentpick.mjs: priority is now explicit > the user own mmx > ours > default agent.
- server.mjs: when no dedicated agent exists, the runtime adds ours and moves her session onto it
  (only while she has not spoken yet, so no history is lost); any failure degrades silently to the
  previous behaviour (the identity sheet travels with the message).

Three hard lines
1. Only ADD <agent dir>/mmx-octopus-agent/agent.md - no existing agent is modified; if the name is
   taken by an agent that is not ours we write nothing and do not use it.
2. The Host agent directory is located only from Host-returned paths; if it cannot be identified we
   write nothing (no fallback guess).
3. Every failure (unknown directory / name taken / write error / verify error) returns an error
   result - it never throws and never blocks her from talking.

Disclosure
Both READMEs now state exactly which file is written, what is in it, that nothing else is
touched, how it is written (atomic, once, only when absent) and how to remove it.

Verification
- Node self-test: 848 checks, 0 failures (new group "round 44", 7 checks, including 4 negative
  injections: dropping the "never touch an existing file" guard / pointing the target at the
  user own agent / deriving the directory from dataDir / breaking the agent priority).
- The previous "never writes a Host file" checks were rewritten as behaviour assertions - they now
  assert that the only module allowed to touch an agent file is agentprov.mjs, and that its target
  is the one agent we own.
…ritten in place

The previous wording ("how to turn it off") was misleading: deleting the file does not
keep it away, the runtime adds it again on the next start. Both READMEs now state the
actual behaviour:

- the file is never rewritten in place, so after a package upgrade the old character
  sheet stays there until the directory is deleted (then it is added again with the
  current sheet);
- to not have this agent at all, create an agent of your own named mmx - the plugin
  prefers it and adds nothing.

Also adds the negative judgement for the rename route (changing her name writes only
state.json and never touches an agent file) to the internal self-test, and states the
judgement in the PR body.

@MyPrototypeWhat MyPrototypeWhat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. This push only touches the README section on removing the agent, so the points below still apply.

A dedicated agent is a reasonable way to keep the persona in the system prompt, and the create-only write, the ownership marker and the refusal to reuse a taken name are all good. One thing needs to change before it can merge.

Must fix

1. The agent directory is found by guessing, and the guess can land in a user's project. agentsRootCandidates() (miniapp/node/agentprov.mjs:107-138) collects every path-like string from the session detail and the agent list. The second pass (agentprov.mjs:128-129) walks up to eight levels from those paths and takes the first folder named agents. On the default agent the session workspace can be a user project, so ~/code/agents/foo would get a new mmx-octopus-agent/agent.md. That contradicts "not your project files" in README.md:133.

Please accept only a root you can prove: the miniapps/<plugin-id> derivation from the first pass, or one derived from context.dataDir as in 1.4.0. Drop the walk-up, and write nothing if neither applies (you already handle that case).

2. The version doesn't match. Both READMEs still say 1.7.0 (README.md:8, README.zh-CN.md:7), while plugin.json and package.json say 1.8.0.

Optional

  • With no agent directory and no session, ensureOwnAgent() creates an extra session on the default agent just to probe paths (miniapp/node/server.mjs:1627). It may show up in the user's session list, so mention it in the README or reuse it as her session.
  • npm run check warns about process.stdout.write in agentprov.mjs. Moving the CLI into a script outside miniapp/node would clear it.
  • Opting out currently means creating an empty mmx agent by hand. A setting in the panel would be easier.

Verified

  • Idle talk still defaults to off (state.mjs:352).
  • The new file holds only the packaged persona and identity sections, and the README says they go into the system prompt.
  • Other writes stay under context.dataDir, and no existing agent.md or AGENTS.md is modified.
  • npm run check passes with one warning (87 tests).

Happy to take another look once the directory lookup is tightened.

Addresses the review on MiniMax-AI#107.

Must fix 1 - the agent directory must be provable, not guessed.
agentsRootCandidates() used to collect every path-like string and then walk
up to eight levels looking for a folder named "agents". On the default agent
the session workspace can be a user project, so ~/code/agents/foo would have
received a new mmx-octopus-agent/agent.md - which contradicts "not your
project files" in the README.

The root now comes from exactly one shape: the workspace of a session the
plugin itself created. The Host sets that workspace to the Mini App's own
agent directory (<agents root>/<agent>/miniapps/<plugin-id>), and the plugin
takes its parent. The walk-up is gone, the agent list is no longer a path
source, and context.dataDir is still never used to derive it. When the
directory cannot be identified, nothing is written and the per-turn
character sheet keeps working.

Must fix 2 - both READMEs said 1.7.0 while plugin.json and package.json said
1.8.0; they now say 1.8.0.

Optional - the probe session is reused as her session instead of creating a
second one, and the README documents it; the CLI moved to
miniapp/scripts/agentprov-cli.mjs, which clears the stdout warning from
npm run check (now 0 warnings for this package, 87/87 tests pass).
@antianqi

antianqi commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review - both Must fixes are addressed in e7415ab (details in the updated PR description): the Host agent directory is now taken from exactly one provable path (the workspace of a session the plugin itself created), the eight-level walk-up that could land in a user project is gone, both READMEs now say 1.8.0, the probe session is reused as her session, and the CLI moved to miniapp/scripts/agentprov-cli.mjs so npm run check reports no warnings for this package. The opt-out switch is the one item I left out; say the word and I will add it.

…ites nothing

The review asked how a user could opt out of "the plugin adds an agent to the
Host". The only answer was "create an empty agent named mmx by hand" — not
easier. The panel now has a two-way choice:

- "use an existing agent" (the default) — the plugin adds nothing and writes
  not a single byte outside its own dataDir;
- "create a dedicated agent" — only then does it add
  <agents root>/mmx-octopus-agent/agent.md. If that fails (name taken, the
  directory cannot be proven, the write fails), the panel says why instead of
  silently falling back.

Also fixes an inconsistency found while writing the disclosure: in the
dedicated tier the agent name was handed to the picker without checking that
it was really ours, so a foreign agent that happened to own the name
mmx-octopus-agent could be used as if it were ours, while the panel said
"could not create it, continuing with an existing agent".
@antianqi

antianqi commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review - the opt-out item (Optional 3) is now implemented in ba3bb32. Rather than a switch, the panel now has a two-way choice:

  • Use an existing agent (the default): the plugin adds nothing and writes not a single byte outside its own data directory - the v1.7.0 behaviour.
  • Create a dedicated agent: only then is <Host agent dir>/mmx-octopus-agent/agent.md created, exactly as described in the PR. If that cannot be done (name taken / directory not provable / write or verify failed), the panel says why instead of silently falling back.

Both READMEs now state that nothing is added by default and that the file is only created after picking create a dedicated agent. The old workaround (create an agent named mmx by hand) is gone - the panel is the only entry point.

Two things worth flagging:

  • Switching the choice restarts her session (the Host cannot change the agent of a live session); the new session opens with a short recap so the thread continues.
  • The dedicated tier now uses that agent only if the file really is ours. While writing the disclosure I found that the picker could still select a foreign agent that happened to own the name mmx-octopus-agent, while the panel said could not create it, continuing with an existing agent - that is fixed and covered by a check.

Self-test: 858 checks, 0 failures (new group round 45, 7 checks, 2 negative injections; the judgements were falsified against the real code as well). npm run check reports plugins/antianqi/mmx-octopus: OK with no warnings, 87/87 tests pass.

The choice was only in the settings panel, so a first-time user never met it.
It now sits on the opening card as well - pick the tier first, then her name
and how she addresses you - and it goes out in the same submission.

Both places share one rendering (the `data-agentmode-group` marker plus a
single `paintAgentMode` selector) so the two copies cannot drift, and while
the opening card is open a snapshot refresh leaves the pending pick alone.

Self-test: 865 checks, 0 failures (new group *round 46*, 7 checks). Every
check was falsified against the real code.
@antianqi

antianqi commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up to the review thread: the choice is now asked on first run as well (commit de64134).

The two-way choice used to sit only in the settings panel, so a first-time user never met it. The opening card now asks for the tier first (use an existing agent / create a dedicated agent), then for her name and how she addresses you, and all three go out in one submission.

Since the same control now exists in two places, both share one rendering (a data-agentmode-group marker plus a single paintAgentMode selector), and a snapshot refresh no longer overwrites the tier the user is still choosing on the card.

Self-test: 865 checks, 0 failures (new group round 46, 7 checks, each falsified against the real code). npm run check reports plugins/antianqi/mmx-octopus: OK with no warnings, 87/87 tests pass.

The product default is now "create a dedicated agent" (boss, 2026-11-04):
her character sheet then lives in that agent's system prompt, so her
personality is crisper and her replies more lifelike. The two-way
choice stays: pick "use an existing agent" and the plugin writes not a
single byte outside its own data directory.

The point of this change is not one constant: "what the default is" now
lives in exactly one place (DEFAULT_AGENT_MODE + normalizeAgentMode in
node/agentpick.mjs). The state machine, the server route and the panel
all go through it; the page reads the value injected by the server, so
nobody writes a second default that can drift.

The READMEs now say the same thing: by default the plugin adds one agent
file, and the "use an existing agent" tier is the zero-write opt-out.
@antianqi

antianqi commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Correction: the product default is now create a dedicated agent (commit 0349167)

This follow-up corrects the statements in my two comments above and in the earlier PR description. They are not wrong about what the code did at that time, but they are no longer the current behaviour.

What changed. The default tier is now create a dedicated agent, not use an existing agent. The reason is the one the owner asked for: a dedicated agent is where her character sheet lives as a system prompt, so her personality is crisper and her replies more lifelike, instead of travelling inside every message where the Host context compaction may drop it.

So these earlier statements no longer hold:

  • "Use an existing agent (the default): the plugin adds nothing and writes not a single byte outside its own data directory" - that is now the opt-out, not the default.
  • "Both READMEs now state that nothing is added by default" - both READMEs now state that by default one agent file is added, and that use an existing agent is the zero-write opt-out.
  • "Nothing is added by default any more" (Known limits) - replaced accordingly.
  • The self-test count in the last comment (865) has grown to 875 checks, 0 failures.

What did not change. The two-way choice is still there, it is still asked on first run, and picking use an existing agent still means the plugin writes not a single byte outside its own data directory and never touches the agent it could have added. The three hard lines stand: still only add (never modify an existing agent), still never guessed (one provable path only), still never throws (every failure degrades and says why).

How it is built. "What the default is" now lives in exactly one place - DEFAULT_AGENT_MODE and normalizeAgentMode in node/agentpick.mjs. The state machine, the server route and the panel all go through it, and the panel reads the value the server injects, so no second default can drift. Before this that rule was written in nine places, and the server.mjs fallback even pointed the other way.

The checks judge the assembled behaviour, not source text: they run the real PetState, POST to the real /api/agent-mode route, and execute the panel`s own normalization function. Six falsification experiments all turn the judgement red - including the one that matters most, hard-coding the default in the page instead of reading the injected value.

A weakness of my own check, found and fixed while doing this: the first version of the page check compared the page result against the server result, and since both defaults were own, a page that hard-coded the default still passed - the check was fake. It now requires the page to follow the injected value, so the two can no longer be told apart by accident.

One honest limit worth stating: an installation that already ran keeps the tier it stored. The new default applies to a fresh install; if an earlier run already stored use an existing agent, that tier stays until the owner picks create a dedicated agent in the panel - one click, and the panel is the only place it lives.

The PR description has been updated with a section covering all of this.

@antianqi

antianqi commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Real-machine verification of both tiers (2026-11-04)

The two tiers were exercised on a live install - clicking in the panel, not only reading the code:

  • Dedicated tier: picking create a dedicated agent and sending one message produced <Host agent dir>/mmx-octopus-agent/agent.md (9548 bytes, carrying the <!-- mmx-octopus:self-agent --> marker). Her session then ran on agentName: mmx-octopus-agent, the Host listed the agent with display name 小章鱼, and the character sheet - persona, appearance and the 28 speech rules - was in that agent`s system prompt.
  • Existing tier: switching to use an existing agent wrote nothing - the file`s timestamp and size were unchanged - and switching back did not rewrite it either (only add, never rewrite).
  • Nothing outside the agent dir: every file changed under the Host data directory during the test was swept; exactly one new host-side file appeared (the agent file above). Everything else was the plugin`s own data directory.

One timing detail, stated plainly because it is real behaviour and not a defect: switching the tier records the choice; the file is created on the next turn that goes through the session path (a message, idle talk, a diary entry). Creating it at the moment of the click is not generally possible, because the Host agent directory can only be proven from a session the plugin created itself - a fresh install has none yet, so it would fail with could not identify the Host agent directory and then create the file on the next turn anyway. Deferring it is what actually succeeds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants