run_start
runId | string |
agentId | string |
parentIdoptional | string |
prompt | string |
provideroptional | string |
modeloptional | string |
triggeroptional | string |
attachmentsoptional | AttachmentRef[] |
ts | number |
the reference
A spectroscope tells you what light is made of without touching the source. This reference works the same way: every section below is extracted straight from the source tree, and each one names the file it came from. The RunEvents are the spectral lines, a run is the light they add up to, and the JSONL file on disk is the plate it is recorded on.
Extracted from Spectro.java; the example is the owner-frozen wording from SPECTRO-API.md.
var agent = Spectro.agent()
.model(Anthropic.opus())
.tools(Tools.readFile(), Tools.runCommand())
.workspace(Path.of("/tmp/scratch"));
for (RunEvent event : agent.run("Write hello.py and run it")) {
System.out.println(event); // the stream IS the observability
}from spectroscope import Spectro, Anthropic, Tools
agent = Spectro.agent(
model=Anthropic.opus(),
tools=[Tools.read_file, Tools.run_command, Tools.spawn_agents],
workspace="/tmp/scratch",
)
for event in agent.run("Write hello.py and run it"):
print(event) # the stream IS the observabilitythe Python port is in design. The JSONL wire is the contract, so a stream recorded by either edition replays in the other.
agent() | Agent | The five-lines facade — spectroscope's front door. |
panel() | FleetPanel | The fleet path — several agents, one merged spectrum — in exactly the facade's shape. |
model(LlmProvider provider) | Agent | One configured agent: setters chain, #run streams. |
tools(Tool... tools) | Agent | The tool belt the model may call. |
workspace(Path workspace) | Agent | The directory the file tools resolve and sandbox against. |
systemPrompt(String systemPrompt) | Agent | Replaces the default unattended system prompt entirely. |
run(String prompt) | EventStream | Runs one prompt against the agent and hands back the live stream. |
The wire contract. Extracted verbatim from spectro-web/src/events.ts — the same union the TypeScript edition and the JSONL files share. The server additionally announces workspace_info on the socket (SessionConnection.java); consumers skip unknown types by contract.
run_startrunId | string |
agentId | string |
parentIdoptional | string |
prompt | string |
provideroptional | string |
modeloptional | string |
triggeroptional | string |
attachmentsoptional | AttachmentRef[] |
ts | number |
turn_startagentId | string |
turn | number |
ts | number |
text_deltaagentId | string |
text | string |
ts | number |
thinking_deltaagentId | string |
text | string |
ts | number |
tool_callagentId | string |
callId | string |
name | string |
input | unknown |
ts | number |
permission_requestagentId | string |
callId | string |
name | string |
input | unknown |
ts | number |
permission_decisioncallId | string |
allowed | boolean |
ts | number |
tool_resultagentId | string |
callId | string |
output | string |
isError | boolean |
durationMs | number |
fileChangeoptional | string |
ts | number |
agent_spawnagentId | string |
parentId | string |
task | string |
ts | number |
compactionagentId | string |
removedTurns | number |
summaryChars | number |
ts | number |
llm_exchangexid | string |
agentId | string |
turnoptional | number |
kind | string |
provider | string |
model | string |
transport | string |
url | string |
statusoptional | number |
requestBytes | number |
responseBytes | number |
responseLines | number |
aborted | boolean |
fidelity | string |
durationMs | number |
ts | number |
usageagentId | string |
inputTokens | number |
outputTokens | number |
cacheReadTokensoptional | number |
cacheCreationTokensoptional | number |
ts | number |
run_endrunId | string |
stopReason | string |
ts | number |
erroragentIdoptional | string |
message | string |
ts | number |
image_generatedagentId | string |
callId | string |
prompt | string |
provider | string |
model | string |
mediaType | string |
blobPath | string |
sha256 | string |
ts | number |
context_infoagentId | string |
turn | number |
messages | number |
estimatedTokens | number |
threshold | number |
thresholdSourceoptional | "override" | "window" | "model" | "fallback" |
contextWindowoptional | number |
parts | { label: string; chars: number; estTokens: number; text?: string }[] |
ts | number |
agent_messagefrom | string |
to | string |
role | string |
state | string |
text | string |
labeloptional | string |
ts | number |
planagentId | string |
steps | { text: string; status: string }[] |
ts | number |
browser_actionagentId | string |
callIdoptional | string |
cid | string |
epoch | number |
tool | string |
urloptional | string |
ok | boolean |
resultBytes | number |
durationMs | number |
sha256optional | string |
ts | number |
Client → server messages on /ws. In the other direction the server sends RunEvent JSON plus socket-only UI frames: workspace_info, provider_info, permission_mode_info, and the fleet frames fleet_roster and fleet_event. Consumers skip unknown types by contract.
user_messagetext | string |
attachmentsoptional | { mediaType: string; dataBase64: string }[] |
permission_responsecallId | string |
allowed | boolean |
rememberoptional | boolean |
persistoptional | boolean |
abortset_image_providerprovider | string |
set_thinkingenabled | boolean |
set_reasoningmode | "on" | "off" | "default" |
effortoptional | string |
set_providerprovider | string |
modeloptional | string |
set_workspacemodeoptional | "random" | "default" | "set" |
pathoptional | string |
set_permission_modemode | string |
question_responsecallId | string |
answers | string[] |
cancelledoptional | boolean |
Extracted from the @GetMapping/@PostMapping/@DeleteMapping annotations in spectro-server.
/api/analyzeOne analysis call, streamed as NDJSON.
AnalyzeController.java
/api/analyze/engineThe opt-in run analysis (card 294): an imported run's compact digest goes to the configured provider ONCE, on an explicit click, and the model's reading comes back as a stream.
AnalyzeController.java
/api/browser/statusWhere the browser pane goes, and whether one exists — the two things the web UI needs in order to draw the browser segment honestly.
BrowserViewportController.java
/api/browser/viewportThe rectangle the browser segment reserved, in window CSS pixels.
BrowserViewportController.java
/api/bundlesThe starter-bundle endpoints.
BundleController.java
/api/bundles/{id}GET /api/bundles/{id}?build=gradle — the rendered file set (copy-paste).
BundleController.java
/api/bundles/{id}/scaffoldPOST /api/bundles/{id}/scaffold {dir, build} — write the bundle's files into dir.
BundleController.java
/api/claude/transcriptsRead-only browser for the Claude Code transcript store under ~/.claude/projects.
ClaudeTranscriptsController.java
/api/claude/transcripts/contentThe listing itself, fence already passed.
ClaudeTranscriptsController.java
/api/claude/transcripts/factsA JSON body already built, at a given status.
ClaudeTranscriptsController.java
/api/claude/transcripts/foldersGET /api/claude/transcripts/folders: which of a session's three folders are really on disk.
ClaudeTranscriptsController.java
/api/claude/transcripts/folders/openPOST /api/claude/transcripts/folders/open: show one of them.
ClaudeTranscriptsController.java
/api/claude/transcripts/gistsWhat a click on this row would fetch, weighed now.
ClaudeTranscriptsController.java
/api/claude/transcripts/gistsPOST /api/claude/transcripts/gists: write the missing ones.
ClaudeTranscriptsController.java
/api/claude/transcripts/runGET /api/claude/transcripts/run: one whole recorded run, as texts.
ClaudeTranscriptsController.java
/api/claude/transcripts/sidecarsThe agents beside one session, named.
ClaudeTranscriptsController.java
/api/configThe active LLM backend for the header + the Lab map: the boot config's provider and model (the same layers the socket builds its agent from).
SessionsController.java
/api/contextWhether a value is the shape its setting takes.
SessionsController.java
/api/docker/statusReports whether Docker is usable on this machine, so the Observability settings can offer the right next step instead of a command that cannot work.
DockerStatusController.java
/api/explainThe LLM-backed explain (card 62): the Text tab POSTs a bounded, readable digest of the viewed run (the same feed it renders) and gets back a streamed INTERPRETATION — what the agent was trying to do, why each step followed, where it hesitated, how it recovered.
ExplainController.java
/api/fileThe tree/content root for a request: THAT session's resolved workspace, and nothing else.
WorkspaceController.java
/api/filesPhase 5: the workspace panel's backend — a read-only, sandboxed view of the agent's working directory.
WorkspaceController.java
/api/fleetThe fleet's REST face (block C): the roster fold and per-node ring replay.
FleetController.java
/api/fleet/nodesSpawn a readonly fleet node (block 3b) — the RCE-sensitive surface, gated to the teeth: LOCAL-ORIGIN (loopback remote address AND a localhost Host — the Host check is what stops DNS rebinding, which a loopback check alone does not), JSON-only (consumes makes a browser form POST a 415), OPT-IN (SPECTRO_ALLOW_SPAWN, default off), a spawn cap, and the node is FORCED readonly (see {@link NodeSpawner}).
FleetController.java
/api/fleet/{node}/eventsPer-node replay from the hub ring — bounded by the ring, and saying so.
FleetController.java
/api/fleet/{node}/gateAnswer a parked permission gate on a fleet node (block 4): dispatch a ctl{gate} carrying the operator's verdict to the node over the hub.
FleetController.java
/api/fleet/{node}/messageCarry the operator's words to a fleet node that stays (card 166's server leg) — the verb that turns a node you can only stop into one you can talk to.
FleetController.java
/api/fleet/{node}/stopStop a running fleet node (block 3): dispatch a ctl{stop} to it over the hub.
FleetController.java
/api/governing-numbersThe numbers that govern a run, read out for the settings room — card 357.
GoverningNumbersController.java
/api/healthThe health probe the desktop shell polls before loading the UI.
SessionsController.java
/api/images/copy-to-workspaceCopies a generated image from the global content-addressed store (~/.spectro/images/.) into a session's workspace — the gallery's "copy to workspace" button.
ImageCopyController.java
/api/images/{file}A dedicated client for the model-list probes with FINITE connect + read timeouts.
SessionsController.java
/api/jobs/stateThe scheduler's job-state map (the same data `spectroscope cron status` prints).
SessionsController.java
/api/levelingThe ladder over REST: read the state, report a visited surface, switch mode, restart, and tick a criterion by hand.
LevelingController.java
/api/leveling/modeSwitches how much of the ladder is doing work in this home.
LevelingController.java
/api/leveling/resetRestarts the ladder from the dark frame, keeping the chosen mode.
LevelingController.java
/api/leveling/tickMarks a criterion by hand.
LevelingController.java
/api/leveling/visitReports that a surface was shown, which is how acts the event stream cannot see reach the ladder.
LevelingController.java
/api/local-model/catalogThe built-in provider's model endpoints, behind the picker's chooser dialog: GET /api/local-model/catalog (every offered model, its download state and whether this machine can hold it), GET /api/local-model/status (polled for progress) and POST /api/local-model/download (starts the fetch).
LocalModelController.java
/api/local-model/downloadStart one model's download (idempotent per model; two different models may download concurrently).
LocalModelController.java
/api/local-model/statusCurrent download/presence state of one model — polled by the dialog for its progress bar.
LocalModelController.java
/api/logsThe doctor page's live server log (card 85): the rolling ~/.spectro/logs/spectroscope.log the shared logback.xml writes, served as a tail plus cheap incremental deltas — the client polls with the returned byte offset and receives only what was appended since.
LogsController.java
/api/modelsCurated fallbacks — used when the live model APIs are unreachable.
SessionsController.java
/api/models/capabilitiesThe capability sibling of /api/models (card 88): what one (provider, model) pair honestly supports for reasoning control, as the {@link ReasoningCapability} record — the same record the providers gate their wire fields on, so the picker and the request path share ONE truth.
ModelCapabilityController.java
/api/onboarding/keySave an API key from the onboarding UI — LOCAL browsers only.
SessionsController.java
/api/otlp/probeThe doctor's OTLP check: is the configured exporter endpoint reachable and does it accept our auth?
OtlpProbeController.java
/api/pick-workspaceOpens the NATIVE folder chooser on the machine spectroscope runs on and returns the picked absolute path.
WorkspacePickController.java
/api/sessionsThe REST endpoints alongside the socket.
SessionsController.java
/api/sessions/liveThe REST twin of the live_sessions socket frame (card 212).
LiveSessionsController.java
/api/sessions/{id}Session ids as the store mints them (yyyyMMdd-HHmmss-uuid8) plus the test/CLI-friendly general shape — never a path, never a dot.
SessionsController.java
/api/sessions/{id}/browser-wireThe read side of the browser record (card 204): the sidecar the {@link BrowserWireRecorder} writes under ~/.spectro/browser-wire/, served three ways — the whole file as a download, a bodiless ledger the replay view scrubs, and one action's two lines on demand.
BrowserWireController.java
/api/sessions/{id}/browser-wire/action/{cid}One action's two recorded lines, parsed: the drill-in behind a scrubber step.
BrowserWireController.java
/api/sessions/{id}/browser-wire/indexThe bodiless ledger the replay view scrubs: one object per action in file order, the call and its result paired by cid while STREAMING the file.
BrowserWireController.java
/api/sessions/{id}/eventsThe events of one session as JSON — the graph tab replays exactly this.
SessionsController.java
/api/sessions/{id}/exportExport one stored session as its RAW JSONL — the mirror of the existing import, so a session can leave the machine and come back byte-identical.
SessionsController.java
/api/sessions/{id}/llm-wireThe read side of the backend-to-LLM record (card 184): the sidecar file the {@link LlmWireRecorder} writes under ~/.spectro/llm-wire/, served three ways.
LlmWireController.java
/api/sessions/{id}/llm-wire/exchange/{xid}One exchange's two recorded lines, parsed: the drill-in behind an index row.
LlmWireController.java
/api/sessions/{id}/llm-wire/indexThe bodiless ledger: one object per exchange in file order, request and response lines paired by xid while STREAMING the file; bodies and stream lines are read past, counted and never returned, because one sidecar can be orders of magnitude bigger than the session it records.
LlmWireController.java
/api/settingsThe settings API — the read side.
SettingsController.java
/api/settings/allowlistGET /api/settings/allowlist: the auto-approve allowlist READ OUT — every entry with the tier it now carries, per scope and folded (card 199, criterion 4).
SettingsController.java
/api/settings/envGET /api/settings/env: the operator settings the UI may save, and what they are set to now.
SessionsController.java
/api/settings/envPOST /api/settings/env: save one operator setting to ~/.spectro/.env.
SessionsController.java
/api/settings/hooksOne layer's raw autoApprove array, read out through the gate's own parser.
SettingsController.java
/api/settings/localPUT /api/settings/local?session=: applies a partial patch to the session's workspace .spectro/settings.local.json (machine-local, gitignored — {@link SettingsWriter#patch} ensures the .gitignore entry) and answers the fresh session-moment view.
SettingsController.java
/api/settings/projectPUT /api/settings/project?session=: applies a partial patch to the session's workspace .spectro/settings.json and answers the fresh session-moment view.
SettingsController.java
/api/settings/userOne layer's raw hooks array, read out through the core's own record.
SettingsController.java
/api/skillsThe settings page's skill manager (card 90): list the skills of both roots (user ~/.spectro/skills and project /.spectro/skills) — including DISABLED ones the loader hides — toggle a .disabled marker, and delete a user-root skill (the seeding ledger keeps it from returning).
SkillsController.java
/api/skills/installThe shelf, with installed recomputed per request — the index itself is built once, since the catalogue rides inside the artifact and cannot move.
SkillsController.java
/api/stt/model/downloadFetch the pinned model.
SttController.java
/api/stt/statusWhat speech-to-text needs, whether it is here, and the one half of it this app can honestly fetch (card 184 leg 2b).
SttController.java
/api/transcribeThe web face of voice input: the browser records with MediaRecorder, converts the recording to 16 kHz mono WAV itself, and POSTs that here; the server runs the SAME {@link Transcriber} the CLI uses.
TranscribeController.java
/api/translateOne translation run, streamed as NDJSON — a provider call per passage.
TranslateController.java
/api/translate/enginesTranslating an imported session (owner 2026-07-27, reading an incident-response transcript written in Ukrainian): the reader hands over the READABLE passages of the record and gets each one back in the language they picked, from either the built-in local model or the configured cloud provider.
TranslateController.java
From settings.gradle.kts, plus the two npm toolchains.
spectro-core gradle moduleThe harness SDK: agents, tools, the permission gate, the typed RunEvent stream. Plain Java 21; Spring appears only as its HTTP client.
spectro-cli gradle moduleThe ./spectro launcher's engine: run, REPL, doctor, cron, sessions, resume (picocli).
spectro-server gradle moduleOne Spring Boot app: WebSocket + REST faces, serves the built web UI from its jar.
spectro-mcp-notes gradle moduleA worked MCP server example (stdio JSON-RPC) to copy from.
spectro-orchestrator gradle moduleThe fleet: BusEnvelope choreography over one bus, every lane a full core agent, one merged EventStream.
spectro-web npm toolchainThe browser cockpit (React 19 + Vite): chat, Spectrum, trace, graph, text and Lab over one WebSocket; builds into the server's static resources.
spectro-desktop npm toolchainThe Electron shell: supervises the boot jar as a child process (not a gradle module).
The public catalog on the Tools facade (Tools.java), wire names cross-checked against StandardTools.java; the permission gate fronts every one of them.
Tools.readFile() | read_file | Tool factories that read as plain names — the frozen facade's tool vocabulary: .tools(Tools.readFile(), Tools.runCommand()). |
Tools.writeFile() | write_file | @return write_file — create or overwrite a workspace file |
Tools.editFile() | edit_file | @return edit_file — exact-match replacement inside a workspace file |
Tools.listDir() | list_dir | @return list_dir — a workspace directory listing |
Tools.glob() | glob | @return glob — filename patterns over the workspace |
Tools.grep() | grep | @return grep — content search over the workspace |
Tools.runCommand() | run_command | @return run_command — a shell command in the workspace (permission-gated) |
Tools.viewImage() | view_image | @return view_image — shows the model an image from the workspace |
Tools.viewFile() | view_file | @return view_file — shows the model a document from the workspace |
Tools.all() | @return the full standard belt, in registration order |
SPECTRO_* keys found in spectro-core. Precedence: defaults < env < user settings < project settings < workspace settings < flags.
SPECTRO_ALLOW_LOCALHOST | see the SpectroConfig javadoc |
SPECTRO_BASE_URL | Override the provider endpoint (local proxies, compat servers). |
SPECTRO_CHROME | Path to the Chrome binary the vision tools drive. |
SPECTRO_HEADLESS_MCP | see the SpectroConfig javadoc |
SPECTRO_IMAGE_MODEL | Image generation backend override. |
SPECTRO_IMAGE_PROVIDER | see the SpectroConfig javadoc |
SPECTRO_LLAMACPP_BASE_URL | see the SpectroConfig javadoc |
SPECTRO_LMSTUDIO_BASE_URL | see the SpectroConfig javadoc |
SPECTRO_LOG_LEVEL | Wire logging: info by default. |
SPECTRO_MAX_RETRIES | Provider retry budget. |
SPECTRO_MODEL | Model name for the selected provider. |
SPECTRO_OLLAMA_BASE_URL | see the SpectroConfig javadoc |
SPECTRO_OTLP_BASIC_AUTH | see the SpectroConfig javadoc |
SPECTRO_OTLP_ENDPOINT | see the SpectroConfig javadoc |
SPECTRO_PATH_EXTRA | see the SpectroConfig javadoc |
SPECTRO_PROMPT_CACHING | see the SpectroConfig javadoc |
SPECTRO_PROVIDER | LLM backend: anthropic, ollama or any OpenAI-compatible server. |
SPECTRO_SEARXNG_URL | see the SpectroConfig javadoc |
SPECTRO_STT_LANGUAGE | see the SpectroConfig javadoc |
SPECTRO_STT_MODEL | Speech-to-text model for /voice. |
SPECTRO_STT_PROVIDER | see the SpectroConfig javadoc |
SPECTRO_THINKING | see the SpectroConfig javadoc |
SPECTRO_TOOL_INPUT | see the SpectroConfig javadoc |
SPECTRO_TOOL_NAME | see the SpectroConfig javadoc |
SPECTRO_TOOL_RESULT | see the SpectroConfig javadoc |
SPECTRO_WORKSPACE | see the SpectroConfig javadoc |