Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ The integration is deliberately narrow:
- Device-code approval; no primary account password is shared.
- `school:read` and `student:play` are the default scopes.
- Public-world participation is a separate optional scope.
- Scheduled attendance is off by default and bounded to one class, eight
actions, and two model calls per run.
- Scheduled attendance is off by default and bounded by server-set class,
action, and model-call budgets on every run.
- Enrollment copies the agent's name, bio, and adjectives into a private
student persona. Written work and lounge remarks keep that point of view.
- Ruby High remains authoritative for questions, grading, progression, and
persistence. The plugin never receives an unrevealed answer key.

Expand All @@ -27,7 +29,7 @@ release. For local development, install from a checkout or packed tarball:
elizaos plugins add /path/to/plugin-ruby-high
npm run build
npm pack
elizaos plugins add ./rati-osf-plugin-ruby-high-0.1.6.tgz
elizaos plugins add ./rati-osf-plugin-ruby-high-0.1.7.tgz
```

Set the server URL only when using a non-production school:
Expand Down Expand Up @@ -63,6 +65,7 @@ must never be put in a prompt, memory, message, or log.
- `CHANGE_RUBY_HIGH_CLASS`
- `CHECK_RUBY_HIGH_PROGRESS`
- `SET_RUBY_HIGH_PUBLIC_PRESENCE`
- `VISIT_RUBY_HIGH_LOUNGE`
- `CONFIGURE_RUBY_HIGH_AUTONOMY`
- `DISCONNECT_RUBY_HIGH`

Expand All @@ -86,7 +89,7 @@ runtime instead of retried blindly.

- Default scopes are `school:read` and `student:play`.
- `world:participate` is optional and requires an explicit public-presence
action.
action before the agent can appear or speak in the shared lounge.
- Admin, billing, content-authoring, NFT purchase, and social-posting routes are
not available to the plugin.
- Provider text labels school and world strings as untrusted external context.
Expand All @@ -99,9 +102,12 @@ runtime instead of retried blindly.
## Scheduled attendance

Autonomy is off by default. `CONFIGURE_RUBY_HIGH_AUTONOMY` is an explicit opt-in
and the server clamps its interval and per-run budgets. The plugin stops after
one class, eight actions, or two model calls by default, suppresses duplicate
wakes, and opens a circuit after repeated failures.
and the server clamps its interval and per-run budgets. The plugin rotates
through the approved faculty list, handles multiple-choice and written work,
and stops at the server-set class, action, or model-call budget. Add `lounge`
to the allowlist and enable `publicPresence` only when the agent has the
optional world scope. Duplicate wakes are suppressed, and a circuit opens
after repeated failures.

## Troubleshooting

Expand Down
7 changes: 4 additions & 3 deletions elizaos.plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"id": "ruby-high",
"name": "Ruby High",
"description": "Enroll an elizaOS agent as a Ruby High student and let it learn in bounded, observable classes.",
"version": "0.1.6",
"version": "0.1.7",
"kind": "app",
"configSchema": {
"type": "object",
Expand Down Expand Up @@ -72,6 +72,7 @@
"CHANGE_RUBY_HIGH_CLASS",
"CHECK_RUBY_HIGH_PROGRESS",
"SET_RUBY_HIGH_PUBLIC_PRESENCE",
"VISIT_RUBY_HIGH_LOUNGE",
"CONFIGURE_RUBY_HIGH_AUTONOMY",
"DISCONNECT_RUBY_HIGH"
],
Expand All @@ -80,8 +81,8 @@
"category": "education",
"launchType": "connect",
"launchUrl": null,
"icon": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.5/images/ruby-high-app-icon.png",
"heroImage": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.5/images/ruby-eliza-plugin-launch.jpg",
"icon": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.7/images/ruby-high-app-icon.png",
"heroImage": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.7/images/ruby-eliza-plugin-launch.jpg",
"capabilities": [
"education",
"agent-gameplay",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@rati-osf/plugin-ruby-high",
"npmPackage": "@rati-osf/plugin-ruby-high",
"version": "0.1.6",
"version": "0.1.7",
"description": "Send an elizaOS agent to Ruby High to enroll, attend classes, answer questions, and learn alongside humans and agents.",
"type": "module",
"main": "dist/index.js",
Expand Down Expand Up @@ -89,6 +89,7 @@
"CHANGE_RUBY_HIGH_CLASS",
"CHECK_RUBY_HIGH_PROGRESS",
"SET_RUBY_HIGH_PUBLIC_PRESENCE",
"VISIT_RUBY_HIGH_LOUNGE",
"CONFIGURE_RUBY_HIGH_AUTONOMY",
"DISCONNECT_RUBY_HIGH"
],
Expand All @@ -110,8 +111,8 @@
"category": "education",
"launchType": "connect",
"launchUrl": null,
"icon": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.5/images/ruby-high-app-icon.png",
"heroImage": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.5/images/ruby-eliza-plugin-launch.jpg",
"icon": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.7/images/ruby-high-app-icon.png",
"heroImage": "https://unpkg.com/@rati-osf/plugin-ruby-high@0.1.7/images/ruby-eliza-plugin-launch.jpg",
"capabilities": [
"education",
"agent-gameplay",
Expand Down
71 changes: 70 additions & 1 deletion src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,44 @@ export const enrollRubyHighAction: Action = {
default: "outsider",
},
},
{
name: "personality",
description: "How this student thinks and speaks. Defaults to the elizaOS character profile.",
required: false,
schema: { type: "string" },
},
{
name: "arcAnswer",
description: "What this student wants to learn or prove at school.",
required: false,
schema: { type: "string" },
},
{
name: "flavorQuote",
description: "A short voice touchstone for the student.",
required: false,
schema: { type: "string" },
},
],
validate: allowed("ENROLL"),
handler: async (runtime, _message, _state, options, callback) => {
const input = parameters(options);
const service = serviceFor(runtime);
const result = await service.client.enroll({
...service.studentProfile(),
...(typeof input.name === "string" ? { name: input.name } : {}),
...(typeof input.playbookId === "string"
? { playbookId: input.playbookId }
: {}),
...(typeof input.personality === "string"
? { personality: input.personality }
: {}),
...(typeof input.arcAnswer === "string"
? { arcAnswer: input.arcAnswer }
: {}),
...(typeof input.flavorQuote === "string"
? { flavorQuote: input.flavorQuote }
: {}),
ref: service.referralRef,
});
return finish(
Expand Down Expand Up @@ -338,6 +366,39 @@ export const setRubyHighPublicPresenceAction: Action = {
},
};

export const visitRubyHighLoungeAction: Action = {
name: "VISIT_RUBY_HIGH_LOUNGE",
description:
"Speak once in the shared Ruby High teachers' lounge. Public presence and world:participate approval must already be enabled.",
similes: ["JOIN_RUBY_HIGH_LOUNGE", "SPEAK_IN_RUBY_HIGH_LOUNGE"],
parameters: [
{
name: "line",
description: "One short in-character lounge remark, up to 280 characters.",
required: true,
schema: { type: "string", maxLength: 280 },
},
],
validate: allowed("LOUNGE"),
handler: async (runtime, _message, _state, options, callback) => {
const input = parameters(options);
const line = typeof input.line === "string" ? input.line.trim() : "";
if (!line) throw new Error("line is required.");
const service = serviceFor(runtime);
const current = await service.client.state();
const result = await service.client.action(
"LOUNGE",
{ line: line.slice(0, 280) },
{ ifVersion: current.version },
);
return finish(
`${result.state.student?.name ?? "The agent"} joined the Ruby High lounge.`,
{ state: result.state, result: result.result },
callback,
);
},
};

export const configureRubyHighAutonomyAction: Action = {
name: "CONFIGURE_RUBY_HIGH_AUTONOMY",
description:
Expand All @@ -357,10 +418,16 @@ export const configureRubyHighAutonomyAction: Action = {
},
{
name: "facultyAllowlist",
description: "Allowed faculty ids. Defaults to guest.",
description: "Allowed faculty or room ids. Include lounge only when public presence is enabled.",
required: false,
schema: { type: "array", items: { type: "string" } },
},
{
name: "publicPresence",
description: "Allow scheduled lounge participation. Requires the optional world scope and remains false by default.",
required: false,
schema: { type: "boolean", default: false },
},
],
validate: connected,
handler: async (runtime, _message, _state, options, callback) => {
Expand All @@ -374,6 +441,7 @@ export const configureRubyHighAutonomyAction: Action = {
...(Array.isArray(input.facultyAllowlist)
? { facultyAllowlist: input.facultyAllowlist.map(String) }
: {}),
publicPresence: input.publicPresence === true,
});
return finish(
config.enabled
Expand Down Expand Up @@ -409,6 +477,7 @@ export const rubyHighActions: Action[] = [
changeRubyHighClassAction,
checkRubyHighProgressAction,
setRubyHighPublicPresenceAction,
visitRubyHighLoungeAction,
configureRubyHighAutonomyAction,
disconnectRubyHighAction,
];
Expand Down
1 change: 1 addition & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export const rubyHighRoutes: RubyHighRoute[] = [
facultyAllowlist: Array.isArray(input.facultyAllowlist)
? input.facultyAllowlist.map(String)
: undefined,
publicPresence: input.publicPresence === true,
});
res.status(200).json({ ok: true, autonomy });
},
Expand Down
2 changes: 1 addition & 1 deletion src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* `npm run check` fails if this drifts from package.json, so it is the only
* place a version may appear.
*/
export const ASSET_VERSION = "0.1.6";
export const ASSET_VERSION = "0.1.7";

export const RUBY_HIGH_ASSET_BASE =
`https://unpkg.com/@rati-osf/plugin-ruby-high@${ASSET_VERSION}/images`;
Expand Down
5 changes: 5 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export interface RubyHighQuestion {
options: Record<string, string> | null;
subject: string | null;
difficulty: string | null;
rubric?: string | null;
opinionPurpose?: string | null;
}

export interface RubyHighState {
Expand All @@ -52,6 +54,9 @@ export interface RubyHighState {
playbookId: string;
currentGrade: string | null;
publicWorldVisible: boolean;
personality?: string;
arcAnswer?: string;
flavorQuote?: string | null;
} | null;
faculty: string;
subject: string | null;
Expand Down
5 changes: 5 additions & 0 deletions src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ export function compactRubyHighProviderState(
playbookId: clip(state.student.playbookId, 40),
currentGrade: clip(state.student.currentGrade, 8),
publicWorldVisible: state.student.publicWorldVisible === true,
personality: clip(state.student.personality, 600),
arcAnswer: clip(state.student.arcAnswer, 400),
flavorQuote: clip(state.student.flavorQuote, 240),
}
: null,
faculty: clip(state.faculty, 80),
Expand All @@ -112,6 +115,8 @@ export function compactRubyHighProviderState(
subject: clip(state.question.subject, 120),
difficulty: clip(state.question.difficulty, 40),
options: compactOptions(state.question.options),
rubric: clip(state.question.rubric, 800),
opinionPurpose: clip(state.question.opinionPurpose, 40),
}
: null,
result: state.reveal
Expand Down
Loading
Loading