Skip to content

refactor(agent-service): extract WebSocket message types into types/ws#5751

Open
bobbai00 wants to merge 10 commits into
apache:mainfrom
bobbai00:refactor/agent-service-foundation
Open

refactor(agent-service): extract WebSocket message types into types/ws#5751
bobbai00 wants to merge 10 commits into
apache:mainfrom
bobbai00:refactor/agent-service-foundation

Conversation

@bobbai00

@bobbai00 bobbai00 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

This PR refactors the code of agent-service ↔ frontend WebSocket messaging definition.

Specifically, a new folder types/ws is added and is dedicated for websocket related message definition.

  • types/ws/client.ts holds the messages the frontend sends to agent-service. They share a base interface WsClientRequestBase (which only carries the type field), and form the union WsClientRequest with two cases:
    • WsClientRequestPrompt — a user prompt to run (type: "prompt").
    • WsClientRequestStopCommand — a request to stop the current run (type: "command", commandType: "stop").
  • types/ws/server.ts holds the messages agent-service sends back to the frontend. They share a base interface WsServerMessageBase, and form the union WsServerMessage with these cases:
    • WsServerSnapshotMessage ("snapshot") — the full state, sent once when a client connects.
    • WsServerStepMessage ("step") — one step, sent live as the agent runs.
    • WsServerStatusMessage ("status") — a state change, e.g. GENERATING / AVAILABLE / STOPPING.
    • WsServerErrorMessage ("error") — an error to show the user.
    • WsServerHeadChangeMessage ("headChange") — deprecated and unused; removed later in refactor(agent-service): remove dead checkout/headChange path #5930.
    • The logic of fetching operator result summaries is changed to be HTTP-based. So none of the above messages' payload carries operator result summaries.

This PR also adds several test cases to ensure the coverage, and changes the filenames of test files in agent-service from *.test.ts to *.spec.ts to align with frontend's naming convention.

Any related issues, documentation, discussions?

Closes #5749

How was this PR tested?

Several unit tests are added and passed. Also local end2end tests are passed.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Opus 4.8 (1M context)

@codecov-commenter

codecov-commenter commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 55.62%. Comparing base (1c580e5) to head (ee92952).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5751      +/-   ##
============================================
+ Coverage     54.60%   55.62%   +1.02%     
  Complexity     2927     2927              
============================================
  Files          1109     1109              
  Lines         42828    42769      -59     
  Branches       4608     4603       -5     
============================================
+ Hits          23385    23792     +407     
+ Misses        18081    17596     -485     
- Partials       1362     1381      +19     
Flag Coverage Δ *Carryforward flag
access-control-service 70.44% <ø> (ø) Carriedforward from da817c3
agent-service 44.50% <100.00%> (+10.13%) ⬆️
amber 56.73% <ø> (ø) Carriedforward from da817c3
computing-unit-managing-service 1.65% <ø> (ø) Carriedforward from da817c3
config-service 57.35% <ø> (ø) Carriedforward from da817c3
file-service 58.59% <ø> (ø) Carriedforward from da817c3
frontend 48.88% <100.00%> (+0.56%) ⬆️
pyamber 90.20% <ø> (ø) Carriedforward from da817c3
python 90.76% <ø> (ø) Carriedforward from da817c3
workflow-compiling-service 58.69% <ø> (ø) Carriedforward from da817c3

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR is a foundational refactor in agent-service/ that centralizes shared DTO/type definitions, introduces a dedicated backend-endpoints config module, and relocates JWT/auth helpers into a focused auth/ module (with new unit tests), while keeping api/backend-api.ts as a transitional entry point.

Changes:

  • Added centralized type modules under src/types/ (api.ts, metadata.ts) and re-exported them from the types barrel.
  • Added src/config/endpoints.ts with getServiceEndpoints() to centralize service base URLs.
  • Moved JWT/auth helpers into src/auth/jwt.ts, updated import paths, and added src/auth/jwt.test.ts.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
agent-service/src/types/metadata.ts Introduces extracted operator-metadata type definitions.
agent-service/src/types/api.ts Adds wire DTOs for backend/service payloads and agent WS protocol.
agent-service/src/types/index.ts Re-exports the new metadata and api type modules.
agent-service/src/config/endpoints.ts Adds a centralized service-endpoint accessor.
agent-service/src/auth/jwt.ts New JWT/auth helper module (moved from api/).
agent-service/src/auth/jwt.test.ts Adds unit tests for JWT helpers.
agent-service/src/server.ts Updates auth import path to the new auth/jwt module.
agent-service/src/api/workflow-api.ts Updates auth header helper import path.
agent-service/src/api/index.ts Updates barrel export to re-export JWT helpers from auth/.
agent-service/src/api/backend-api.ts Re-exports metadata types from src/types/metadata (transitional).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread agent-service/src/auth/jwt.test.ts Outdated
Comment thread agent-service/src/auth/jwt.test.ts Outdated
Comment thread agent-service/src/auth/jwt.test.ts Outdated
Comment thread agent-service/src/types/api.ts Outdated
@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @Ma77Ball, @PG1204, @officialasishkumar
    You can notify them by mentioning @Ma77Ball, @PG1204, @officialasishkumar in a comment.

@bobbai00 bobbai00 changed the title refactor(agent-service): centralize types, endpoint config, and auth module refactor(agent-service): centralize types, endpoint config, and auth module in agent-service Jun 21, 2026
Comment thread agent-service/src/auth/jwt.test.ts Outdated
Comment thread agent-service/src/types/api.ts Outdated
Comment thread agent-service/src/types/ws.ts Outdated
import type { WorkflowContent } from "./workflow";

export interface WsMessage {
type: "message" | "stop";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

rename to prompt and kill-command

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

inherit on interface, define kill-command seperately

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment thread agent-service/src/types/ws.ts Outdated
Comment on lines +46 to +55
export interface WsOutgoingMessage {
type: "step" | "state" | "error" | "complete" | "init" | "headChange";
step?: ReActStep;
state?: string;
error?: string;
steps?: ReActStep[];
headId?: string;
operatorResults?: Record<string, OperatorResultSummaryWs>;
workflowContent?: WorkflowContent;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you need to seperate them into their own definition. don't mix them together.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment thread agent-service/src/types/ws.ts Outdated
state: string;
inputTuples: number;
outputTuples: number;
inputPortShapes?: { portIndex: number; rows: number; columns: number }[];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

inputPortsDataShape?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this be output?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this will be refactored in later's PR #5927

@bobbai00 bobbai00 force-pushed the refactor/agent-service-foundation branch from 6ff0a71 to 9a00461 Compare June 24, 2026 05:41
@github-actions github-actions Bot added engine frontend Changes related to the frontend GUI labels Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 2 better · 🔴 3 worse · ⚪ 10 noise (<±5%) · 0 without baseline

Compared against main 1c580e5 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 385 0.235 25,139/31,427/31,427 us 🟢 -8.0% / 🟢 -10.2%
🔴 bs=100 sw=10 sl=64 799 0.488 122,151/157,858/157,858 us 🔴 +5.6% / 🔴 +12.9%
bs=1000 sw=10 sl=64 928 0.566 1,077,487/1,128,224/1,128,224 us ⚪ within ±5% / 🔴 -10.9%
Baseline details

Latest main 1c580e5 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 385 tuples/sec 402 tuples/sec 410.82 tuples/sec -4.2% -6.3%
bs=10 sw=10 sl=64 MB/s 0.235 MB/s 0.246 MB/s 0.251 MB/s -4.5% -6.3%
bs=10 sw=10 sl=64 p50 25,139 us 23,528 us 23,785 us +6.8% +5.7%
bs=10 sw=10 sl=64 p95 31,427 us 34,160 us 34,980 us -8.0% -10.2%
bs=10 sw=10 sl=64 p99 31,427 us 34,160 us 34,980 us -8.0% -10.2%
bs=100 sw=10 sl=64 throughput 799 tuples/sec 823 tuples/sec 891.94 tuples/sec -2.9% -10.4%
bs=100 sw=10 sl=64 MB/s 0.488 MB/s 0.502 MB/s 0.544 MB/s -2.8% -10.4%
bs=100 sw=10 sl=64 p50 122,151 us 119,101 us 112,277 us +2.6% +8.8%
bs=100 sw=10 sl=64 p95 157,858 us 149,492 us 139,802 us +5.6% +12.9%
bs=100 sw=10 sl=64 p99 157,858 us 149,492 us 139,802 us +5.6% +12.9%
bs=1000 sw=10 sl=64 throughput 928 tuples/sec 937 tuples/sec 1,041 tuples/sec -1.0% -10.9%
bs=1000 sw=10 sl=64 MB/s 0.566 MB/s 0.572 MB/s 0.635 MB/s -1.0% -10.9%
bs=1000 sw=10 sl=64 p50 1,077,487 us 1,065,684 us 972,714 us +1.1% +10.8%
bs=1000 sw=10 sl=64 p95 1,128,224 us 1,107,788 us 1,023,057 us +1.8% +10.3%
bs=1000 sw=10 sl=64 p99 1,128,224 us 1,107,788 us 1,023,057 us +1.8% +10.3%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,519.03,200,128000,385,0.235,25138.52,31426.93,31426.93
1,100,10,64,20,2502.14,2000,1280000,799,0.488,122150.78,157858.47,157858.47
2,1000,10,64,20,21552.44,20000,12800000,928,0.566,1077487.28,1128224.11,1128224.11

@bobbai00 bobbai00 force-pushed the refactor/agent-service-foundation branch from 9a00461 to 2bae592 Compare June 24, 2026 06:52
@github-actions github-actions Bot removed the engine label Jun 24, 2026
@bobbai00 bobbai00 force-pushed the refactor/agent-service-foundation branch from 2bae592 to 508e72c Compare June 24, 2026 07:17
@bobbai00 bobbai00 changed the title refactor(agent-service): centralize types, endpoint config, and auth module in agent-service refactor(agent-service): extract WebSocket message types into types/ws Jun 24, 2026
@bobbai00 bobbai00 force-pushed the refactor/agent-service-foundation branch from 508e72c to bc67f57 Compare June 24, 2026 07:45
Move the inline WS message definitions out of server.ts into a dedicated
types/ws module, modeled as discriminated unions rather than one
all-optional interface:

- client.ts: WsClientRequest = WsClientRequestPrompt | WsClientRequestStopCommand
- server.ts: WsServerMessage union (snapshot/step/status/completion/error/headChange) + OperatorResultSummaryWs
- index.ts: barrel, re-exported from the types barrel

The client->server wire discriminator changes from "message"/"stop" to
"prompt"/"command" (stop becomes commandType: "stop"). The server->client
discriminators are renamed to uniform nouns that name each frame's payload:
init->snapshot, state->status, complete->completion (step/error unchanged),
with interfaces renamed to match (WsServerSnapshotMessage /
WsServerStatusMessage / WsServerCompletionMessage). server.ts parses
WsClientRequest and switches on the new shapes; the frontend WS sends and the
receive switch are updated in lockstep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
@bobbai00 bobbai00 force-pushed the refactor/agent-service-foundation branch from bc67f57 to 43f0b96 Compare June 24, 2026 08:35
The `completion` frame carried both the agent's final `state` and the
operator-results snapshot. That duplicated the state path: the agent's
GENERATING->AVAILABLE transition (in TexeraAgent.sendMessage's finally) was
announced *only* inside `completion`, so `completion.state` and `status`
frames were two routes driving the same client state.

Route the end-of-run state through a `status` frame instead, emitted in a
finally covering both the success and error paths, and slim `completion` to
`{ type, operatorResults }` (a pure final-results snapshot). The frontend
`completion` handler drops its state branch; the `status` handler already
applies the resting state.

Side effect: this also fixes the client staying stuck on GENERATING after an
error — the error path previously emitted no resting-state update.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
bobbai00 and others added 8 commits June 24, 2026 01:48
Convert the per-message comments in types/ws to uniform JSDoc blocks (so they
surface in IDE hovers) and give every client and server frame a brief
purpose line. Mark WsServerHeadChangeMessage @deprecated: it is redundant and
unused (the checkout flow that emits it is unreachable) and is slated for
removal in apache#5930.

Doc-only; no type or runtime change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
…lts on demand

Operator result summaries were pushed over the socket (on snapshot, step, and
completion frames), but the only frontend consumer — the per-operator chat
popover — reads them from a subject that was fed solely by those pushes, and
the step-frame results were never applied at all (dead payload). Most summary
fields are server-side agent context that is never displayed.

Move results to an on-demand pull and slim the socket to conversation +
lifecycle:

- Drop the `completion` frame entirely (type, server broadcast, frontend
  handler). Run-end is already signaled by the end-of-run `status` frame.
- Drop `operatorResults` from the `snapshot` and `step` frames (and stop
  computing/sending them there).
- The chat popover now calls fetchOperatorResults() (GET /operator-results)
  when it opens, pushing fresh summaries to operatorResultSummaries$.
- Remove the dead result-annotations machinery (toggleResultAnnotations,
  resultAnnotationsVisible(Subject/$), getResultAnnotationsVisible) — no callers.

Server frames are now: snapshot, step, status, error (plus the deprecated
headChange, removed in apache#5930).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
Add coverage for the parts of the WS framework this PR introduces, which the
existing app.handle()-based suite never exercised (it only hits HTTP routes):

- server.ws.test.ts: drives the real socket via app.listen + a WebSocket
  client. Covers the results-free snapshot on connect, the unknown-agent
  error, the stop command -> STOPPING status, prompt validation and malformed
  -frame errors, a stubbed prompt run streaming GENERATING -> step -> resting
  status, and a failed run still returning to a resting status (the
  stuck-on-GENERATING fix). No live LLM: the agent's sendMessage is stubbed.
- server.ts: add a small `_getAgentForTests` hook (mirrors
  `_resetAgentStoreForTests`) so tests can stub agent behavior.
- agent.service.spec.ts (frontend): cover fetchOperatorResults — the on-demand
  REST pull that replaced the WS results push — including the failure fallback.

server.ts line coverage rises from ~53% to ~70%; the remaining gaps are
pre-existing (agent construction, the deprecated checkout endpoint, the
startup banner) and not introduced by this PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
Extend the suite to exercise every reachable path in server.ts:

- HTTP routes not previously covered: react-steps, system-info,
  operator-types, steps-by-operators, the operator-results mapping body
  (with a stubbed visible result), checkout (success + not-found), the
  empty-modelType guard, initial-settings-on-create, the non-fatal
  workflow-load failure, delegate-token masking, and the catch-all error
  handler for unknown routes.
- WebSocket branches: a message for an agent that was removed mid-connection,
  the final-step re-broadcast, the broadcast path tolerating a websocket
  whose send throws, and the close handler on disconnect.
- start(): boots the listening app + prints the banner, and tolerates a
  metadata-initialization failure (stubbed to reject).

Also collapse the `if (import.meta.main) start()` entry guard to one line so
the entry point is a covered statement rather than an untestable block.

server.ts is now at 100% line and function coverage (116 tests, all green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
Rename the agent-service test files from *.test.ts to *.spec.ts to match the
frontend convention and codecov.yml's `**/*.spec.ts` ignore rule, so test
files stay out of the coverage denominator. Bun discovers .spec.ts the same
as .test.ts; no test changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
When an operator's chat popover opens (the `element:chat` paper event), the
editor now pulls the active agent's operator results on demand. Add a test
that adds an operator, fires `element:chat` for it, and asserts
fetchOperatorResults is called with the active agent id — the one line of the
PR's frontend change that wasn't yet exercised.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
The frontend CI job runs `prettier-eslint --list-different` before the tests;
a multi-line `httpMock.expectOne(...)` in the added fetchOperatorResults spec
fit on one line under the frontend's wider printWidth, so the check failed and
the job exited before running tests — which is also why no frontend coverage
report was produced. Reformat to satisfy the check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
Codecov flagged two uncovered changes in this PR's frontend:

- agent.service.ts: the stop-command websocket send in stopGeneration() was
  never exercised. Add a spec that injects an open mock socket and asserts the
  `{ type: "command", commandType: "stop" }` frame is sent (plus the REST
  fallback when no socket is open).
- workflow-editor.component.ts: the `if (activeAgentId)` branch in the chat
  popover's results pull was only half-covered. Add a no-active-agent case so
  both branches are exercised.

Verified against the full frontend coverage run: both lines/branches now hit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qFkyrpTd5PrkNBPcBeo4Q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-service frontend Changes related to the frontend GUI refactor Refactor the code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(agent-service): consolidate the WebSocket message-type framework

4 participants