Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ec7b82b
Checkpoint: realism pass, café rooftop, garden, ambience, chess
Oct 3, 2026
d540022
Local snapshot of existing visuals for isolated preview build
Oct 3, 2026
c54fca2
Refine office characters and furnishings with fine voxel geometry
Oct 3, 2026
6805572
Use voxel chairs with existing local standard materials
Oct 3, 2026
ae07550
Restyle dialogs, buttons and popups with a voxel block theme
Oct 3, 2026
a6c154d
Voxel people, workers, block signs and prototype lighting
Oct 3, 2026
029548c
Block hands, picker lighting and voxel rooftop
Oct 3, 2026
344b6c0
Let Claude Code workers choose their model by task size
Oct 3, 2026
52ecd7f
Use the router's tiers and refuse Opus
Oct 3, 2026
353ea21
Chunkier, friendlier block hands
Oct 3, 2026
5403fe2
Clean block hands: single palm block, no skew, poses close properly
Oct 3, 2026
0a1ebab
Rebuild first-person hands as short chunky block hands
Oct 3, 2026
155e5e6
Shaded block hands with knuckle flush and nails
Oct 3, 2026
394111f
Minecraft-style block hands with knuckles, grooves and nails
Oct 3, 2026
2a6a613
Show only the fingertips in first person
Oct 3, 2026
f2f9458
Lounge jukebox plays Quran.com surah recitations
Oct 4, 2026
f54bee1
Quran window: embedded recitation playlist
Oct 4, 2026
a8d5323
A-B loop for the recitation player
Oct 4, 2026
bed51dd
Recitation videos as a list, each with its own A-B loop
Oct 4, 2026
bc999a9
Send the page's origin so YouTube embeds play
Oct 4, 2026
2816959
Voxel world outside: floating islands, terraced clouds, block trees a…
Oct 4, 2026
7516bfc
Vancouver downtown, Stanley Park and landmarks in blocks; voxel traff…
Oct 4, 2026
abeecb8
Weather controller and location picker on the left
Oct 4, 2026
fb0bdb4
Voxel rooftop garden: block plants, structures, ground; fog slider fu…
Oct 4, 2026
e9e9eee
Push sun, moon and stars out to the far plane so city towers no longe…
Oct 4, 2026
ef5cb3a
Show the Vancouver city from every floor, not just the roof
Oct 5, 2026
f4cd627
Every round primitive is now blocks (cars, props, lamps), simple voxe…
Oct 5, 2026
01180a1
Create a GitHub repo and its floor in one step (gh repo create)
Oct 5, 2026
c846c3a
New floors clone straight into <workspace>/<project>
Oct 5, 2026
7aae1e2
Clone tests follow the flat project folder layout
Oct 5, 2026
f084605
Reuse a leftover checkout of the same repo; otherwise keep other folders
Oct 5, 2026
baf2b28
Folder hints show <folder>/<repo>
Oct 5, 2026
d39cc80
No cigarette butts in the balcony ashtray
Oct 5, 2026
c2fe1f9
Remove the balcony ashtray and its smoke-break spot
Oct 5, 2026
6054f1c
Replace the office dog with a voxel cat
Oct 5, 2026
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ curl -fsSL https://raw.githubusercontent.com/AgentSystemLabs/agent-office/main/i

- **Other maps.** Turn the whole building into a castle: sit on a throne of iron blades while your workers line up before you when they're done, send new ones off through the Hand of the King, and watch their beards grow long and grey as they toil. Send one home and the Kingsguard runs up from the dungeon, marches it down the stairs and throws it in a cell, where it starves, dies and rots down to a skeleton. Or into a space station in orbit, the Earth turning outside its windows: you run it from the captain's chair on the bridge, and a worker sent home is marched to the airlock and blown out into space, to drift off past the observation windows with everyone who went before it. Or make a map of your own, with its own way of seeing workers off in JSON ([docs/maps.md](docs/maps.md)).

There's a lot more (a rooftop bar, an office dog, an arcade, supercars in the garage to drive round a scenic loop past a farm, pines, mountains and a beach): see [docs/features.md](docs/features.md).
There's a lot more (a rooftop café, an office dog, an arcade, supercars in the garage to drive round a scenic loop past a farm, pines, mountains and a beach): see [docs/features.md](docs/features.md).

## Requirements

Expand Down
27 changes: 25 additions & 2 deletions bin/office-workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export const EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
/** How long the office may take to come back when it's restarting (a dev reload, an upgrade). */
const RETRY_MS = 6000;
/** Sending several workers home waits on git for each; hiring may fetch from GitHub first. */
const TIMEOUT_MS = { list: 15_000, tell: 15_000, pr: 45_000, hire: 90_000, home: 300_000 };
const TIMEOUT_MS = { list: 15_000, tell: 15_000, model: 15_000, pr: 45_000, hire: 90_000, home: 300_000 };
/** Where each call goes, under /office/workers. */
const PATHS = { list: '', hire: '', home: '/home', tell: '/tell', pr: '/pr' };
const PATHS = { list: '', hire: '', home: '/home', tell: '/tell', model: '/model', pr: '/pr' };

/**
* Reads `--flag value` and `--flag=value` options, and the words that aren't options.
Expand Down Expand Up @@ -322,6 +322,24 @@ export const TOOLS = [
},
annotations: { destructiveHint: true, idempotentHint: false, openWorldHint: false },
},
{
name: 'choose_model',
title: 'Choose your model',
description:
'Switches the model you run on (Claude Code /model) to fit the task. Size the task first: high for planning, architecture, hard debugging and reviews; ' +
'medium for ordinary implementation; low for small edits, lookups, renames and chores. Call it at the start of a task, and again when the work changes size ' +
'(plan on high, then build on medium). The switch applies when your current step ends; the conversation carries on.',
inputSchema: {
type: 'object',
properties: {
tier: { type: 'string', enum: ['high', 'medium', 'low'], description: 'How big the task is.' },
model: { type: 'string', description: 'A specific model name instead of a tier.' },
reason: { type: 'string', description: 'A few words on why, shown to the office.' },
},
additionalProperties: false,
},
annotations: { destructiveHint: false, idempotentHint: true, openWorldHint: false },
},
{
name: 'tell_worker',
title: 'Tell a worker',
Expand Down Expand Up @@ -366,6 +384,7 @@ const INSTRUCTIONS =
"rather than looking for the agents with git, ps or HTTP calls. list_workers says where each one's pull request stands (merged: true means it merged), hire_worker " +
'puts a new agent to work, send_home sends agents home and deletes their worktrees and branches, tell_worker gives one a prompt, and link_pr says which pull request is a ' +
"worker's when list_workers does not show it. Everyone in the office sees who did what. " +
'choose_model switches your own model to fit the task: call it when you start a task (high for planning and hard problems, medium for ordinary work, low for small edits and lookups) and again when the work changes size. ' +
'The office-workers command on your PATH does the same from a shell.';

/** Runs a tool; resolves to its text, and whether nothing it was asked came off, or throws with why it failed. */
Expand All @@ -386,6 +405,10 @@ async function runTool(name, args, io) {
const answer = await call('tell', a, io);
return { text: `Told ${answer.worker?.name ?? a.worker}.` };
}
if (name === 'choose_model') {
const answer = await call('model', a, io);
return { text: `Switching to ${answer.model}${answer.tier ? ` (${answer.tier})` : ''}; it applies when this step ends.` };
}
if (name === 'link_pr') return { text: formatLinked(await call('pr', a, io)) };
throw new Error(`Unknown tool: ${name}`);
}
Expand Down
2 changes: 1 addition & 1 deletion docs/code-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ They're in `core/registry.ts`, and each is a field of `ctx`. Every registration
| `ctx.ticks` | What runs each frame. `add(phase, fn)`, where the phases run in `TICK_PHASES` order: `pre`, `steer`, `vehicles`, `move`, `moved`, `play`, `me`, `others`, `world`, `env`, `aim`, `hud`, `render`. Within a phase, ticks run in install order. |
| `ctx.activities` | Something you're in the middle of that takes over the controls (the ladder, the golf tee, a car). It gets keys before the office's own, draws the hint bar, and is stopped by `stopAll(why)` when you start something else. Its place among the others is `ACTIVITY_ORDER` in `core/ctx.ts`. |
| `ctx.interactions` | What each kind of thing you can use does: `define(kind, { reach, hint, use })`, once per kind. |
| `ctx.view` | What what you're doing does to your view each frame: something you hold on to, the field of view, covering the screen, or a filter (the drunk vision). |
| `ctx.view` | What what you're doing does to your view each frame: something you hold on to, the field of view, covering the screen, or a filter. |
| `ctx.windowOpened` | What lets go when a window opens (a shot being wound up, the emote wheel). |
| `ctx.usables` | Things to use that aren't part of the building and move about (the pictures, the dog, the ball): what each has to use, and what the aim can land on. |

Expand Down
5 changes: 3 additions & 2 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ Everything in the office, room by room. Back to the [README](../README.md).
- **Office sounds.** Busy workers clatter away at their keyboards, footsteps knock across the planks (and tick on the garage's concrete, swish through the grass and echo off the castle's flagstones, quicker and harder at a run), the fridge hums, birds chirp outside the windows by day and crickets at night, rain patters on the glass, thunder rolls in a storm, the coffee machine grinds and gurgles, the gong booms when a PR merges and the dog barks. It's all synthesized in the browser and placed where it happens, so it gets louder as you walk closer. Turn it down or mute it under **⚙️**, which also covers the worker dings but not voice chat.
- **Arcade cabinet.** Workers take minutes, so there's an arcade cabinet in the lounge, next to the jukebox. Press **E** at it and the camera glides up to its screen to play BLOCKFALL, a falling-blocks game: arrows (or WASD) move and turn, **Space** drops, **C** holds a piece for later, **P** pauses. Everyone else on your floor sees your game on the cabinet as you play, and anyone can walk up and press **E** to watch it up close over your shoulder. When one of your workers needs input, the game pauses and says which one, with a button to its terminal. Walk away and your game waits, paused, until you come back. The high-score table is the whole building's: it shows on every floor's cabinet, and it survives restarts.
- **Basketball.** Every floor has a hoop on the west wall, by the exit door, with a key painted on the floor in front of it and a ball waiting underneath. Walk up to the ball and press **E** to pick it up. Hold **E** (or the mouse button, in first person) to wind up: a meter runs up and down, and letting go in its green band sinks it, while a little late banks in off the glass and too early or too late misses. In first person the ball goes where you look, so put the crosshair on the rim. In third person your character squares up to the hoop when you're facing roughly its way. **Q** drops the ball. Everyone on the floor sees the ball in your hands and the same shot fly, bounce off the rim and swish through the net, and grab the rebound (**E**, even in mid-air). A basket from past 6.75 m is worth three, and you're told how many you've sunk in a row. The ball stays on its floor: take the elevator with it and it goes back under the hoop.
- **The rooftop bar.** The elevator goes up to the roof, over every floor. A DJ plays drum and bass on a stage under a rig of moving lights and lasers, with an LED wall behind and a dance floor lighting up in front, all flashing in time with the music. The Sky Bar is under a pergola hung with string lights, there's a lounge round a fire pit, and sun loungers look out over the street. The city goes on all around, and its windows, street lamps and traffic light up at night. Press **E** at the bar (or sitting on a stool at it) for a drink: a beer, wine, a cocktail or a shot goes to your head for a minute or so, and the view sways, doubles and smears while you stagger a little. The more you have, the worse it gets, until the bartender pours you a water. Everyone up there sees the glass in your hand and hears the same bar of music. Press **E** at the DJ booth to blow the air horn for all of them.
- **Darts and axe throwing.** In the rooftop bar's north-west corner, past the DJ, there's a wooden axe-throwing booth with a target on its back wall, and a dart board in a cabinet on the outside of it. Walk up to either and press **E** to step up to the line: the camera looks over your shoulder at the target. Move the mouse (or use the arrow keys) to aim; your hand wanders a little, and more after a few drinks from the bar. Hold **Space** (or the mouse button) to draw back while a meter runs up and down, and let go in its green: a dart flies true, too soft and it drops, too hard and it sails high. An axe let go in the green turns over just right and sticks; off it, it comes in handle first, bounces off and clanks onto the mat. A visit is three darts (180 is the most) and an axe round five throws, scored 1 to 4 in the rings, 6 for the bullseye and 8 for either blue killshot dot. Everyone up there sees you throw, the darts and axes stuck in the target, and your round chalked up on the board beside it. Your best round at each is kept in your browser. **E** steps back from the line.
- **The rooftop café.** The elevator goes up to the roof, over every floor. It's a calm, alcohol-free café (and nothing on the menu has pork in it): a plaster and timber counter along the east edge with an espresso machine, a grinder, a glass case of croissants and a chalkboard menu, a barista in an apron, and a whitewashed pergola hung with warm string lights and pendant lamps. Round tables with a chair either side of each, tall tables to stand at and stools along the counter fill the middle, and sun loungers look out over the street. The city goes on all around, and its windows, street lamps and traffic light up at night. Press **E** at the counter (or sitting on a stool at it) for the menu: espresso, cappuccino, flat white, mint or black tea, fresh orange juice, lemonade, sparkling water or hot chocolate, and a butter or almond croissant, a pain au chocolat, a cheese and tomato toastie, avocado toast, a fruit cup, a bagel with cream cheese or a falafel wrap. The barista makes it and slides it over, and you hold it for a minute (a cup, a tall glass or a plate), sipping or nibbling. Everyone up there sees what's in your hand. The south-west corner and a patch against the north edge are kept clear for other things (`ROOF_CLEAR` in `src/shared/layout.ts`).
- **Darts and axe throwing.** In the rooftop café's north-west corner there's a wooden axe-throwing booth with a target on its back wall, and a dart board in a cabinet on the outside of it. Walk up to either and press **E** to step up to the line: the camera looks over your shoulder at the target. Move the mouse (or use the arrow keys) to aim; your hand wanders a little. Hold **Space** (or the mouse button) to draw back while a meter runs up and down, and let go in its green: a dart flies true, too soft and it drops, too hard and it sails high. An axe let go in the green turns over just right and sticks; off it, it comes in handle first, bounces off and clanks onto the mat. A visit is three darts (180 is the most) and an axe round five throws, scored 1 to 4 in the rings, 6 for the bullseye and 8 for either blue killshot dot. Everyone up there sees you throw, the darts and axes stuck in the target, and your round chalked up on the board beside it. Your best round at each is kept in your browser. **E** steps back from the line.
- **Chess at the Merge Conflict corner.** Where the DJ used to play, on the roof's north side, there are three chess tables on a rug under a sign ("Merge Conflict — resolve it over a game of chess"), each with a polished board inlaid in its top and a chair either side. Sit in a chair (**E**) to take that side: the south chair is White, the north chair Black. Whoever sits across from you plays you, and everyone else up there can watch (and sees what's been taken laid out along the table's edges). It's a full game: castling, en passant, promotion (to a queen), check, checkmate, stalemate and draws for too little material or fifty moves without a capture or a pawn move. Look at your own pieces to light them, click one (or press **E** while looking at it) and then one of the glowing squares to move it; the last move and a king in check are lit for everyone. The little flag on your edge of the table resigns (click it twice) and the gold coin sets the pieces up again once a game is over. **W A S D** gets you up, and the game waits for whoever sits there next; a table goes back to the start once everyone has left it. Games live in the office's memory only.
- **Jukebox.** A jukebox glows in the corner of the lounge. Walk up and press **E** to put on one of its four lo-fi tunes, skip to the next, turn it off, or paste a link to internet radio or an audio file. Everyone on the floor hears the same song from the same bar, coming from the jukebox: loud on the couch, faint at the desks. The tunes are synthesized in the browser like the office sounds, and it keeps playing when you switch tabs. The jukebox has its own volume under **⚙️**, just for you.
- **Screen sharing.** Your screen appears on the lounge TV for everyone, and there's a full-screen viewer.
- **An account for everyone.** Open **🔑 Accounts** from the **☰** menu and make an invite link. Whoever opens it picks a password and gets an account in their own name. That name is the one on their character, in chat and on every terminal they type into (**⌨️** in the terminal header shows who typed last), and nobody else can take it. Admins see everyone's accounts there, can make someone an admin, and can revoke an account, which signs that person out at once. The shared office password keeps working alongside the accounts until an admin switches it off. Sessions are signed cookies, and login attempts are rate limited.
Expand Down
Loading